The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 785
MANIFEST 218
MANIFEST.bak 0276
META.yml 11
MYMETA.yml 11
Makefile.PL 11
README.pod 1822
lib/Mojo/Asset/File.pm 139
lib/Mojo/Asset/Memory.pm 85
lib/Mojo/Base.pm 124
lib/Mojo/ByteStream.pm 2929
lib/Mojo/Cache.pm 32
lib/Mojo/Command.pm 4122
lib/Mojo/Content/MultiPart.pm 2013
lib/Mojo/Content/Single.pm 136
lib/Mojo/Content.pm 4531
lib/Mojo/Cookie/Request.pm 105
lib/Mojo/Cookie/Response.pm 45
lib/Mojo/Cookie.pm 22
lib/Mojo/CookieJar.pm 4119
lib/Mojo/DOM.pm 11457
lib/Mojo/Date.pm 105
lib/Mojo/Exception.pm 76
lib/Mojo/Headers.pm 1411
lib/Mojo/HelloWorld.pm 612
lib/Mojo/Home.pm 78
lib/Mojo/IOLoop.pm 838251
lib/Mojo/IOWatcher/Epoll.pm 037
lib/Mojo/IOWatcher/KQueue.pm 0117
lib/Mojo/IOWatcher.pm 0331
lib/Mojo/JSON.pm 1414
lib/Mojo/Loader.pm 10118
lib/Mojo/Log.pm 88
lib/Mojo/Message/Request.pm 3737
lib/Mojo/Message/Response.pm 1216
lib/Mojo/Message.pm 7150
lib/Mojo/Parameters.pm 5148
lib/Mojo/Path.pm 811
lib/Mojo/Resolver.pm 0390
lib/Mojo/Server/CGI.pm 32
lib/Mojo/Server/Daemon.pm 1410
lib/Mojo/Server/FastCGI.pm 2319
lib/Mojo/Server/Hypnotoad.pm 8834
lib/Mojo/Server/Morbo.pm 0204
lib/Mojo/Server/PSGI.pm 76
lib/Mojo/Server.pm 2423
lib/Mojo/Template.pm 2415
lib/Mojo/Transaction/HTTP.pm 3826
lib/Mojo/Transaction/WebSocket.pm 2716
lib/Mojo/Transaction.pm 118
lib/Mojo/Transactor.pm 0347
lib/Mojo/URL.pm 2922
lib/Mojo/Upload.pm 75
lib/Mojo/UserAgent.pm 406155
lib/Mojo/Util.pm 3726
lib/Mojo.pm 66
lib/Mojolicious/Command/Cgi.pm 11
lib/Mojolicious/Command/Daemon.pm 41
lib/Mojolicious/Command/Eval.pm 11
lib/Mojolicious/Command/Fastcgi.pm 11
lib/Mojolicious/Command/Generate/App.pm 21
lib/Mojolicious/Command/Generate/LiteApp.pm 10
lib/Mojolicious/Command/Generate/Makefile.pm 10
lib/Mojolicious/Command/Get.pm 33
lib/Mojolicious/Command/Inflate.pm 11
lib/Mojolicious/Command/Psgi.pm 11
lib/Mojolicious/Command/Routes.pm 43
lib/Mojolicious/Command/Test.pm 11
lib/Mojolicious/Command/Version.pm 11
lib/Mojolicious/Commands.pm 2931
lib/Mojolicious/Controller.pm 5595
lib/Mojolicious/Guides/Cheatsheet.pod 70
lib/Mojolicious/Guides/Cookbook.pod 2133
lib/Mojolicious/Guides/FAQ.pod 93
lib/Mojolicious/Guides/Growing.pod 93
lib/Mojolicious/Guides/Rendering.pod 43
lib/Mojolicious/Guides/Routing.pod 2670
lib/Mojolicious/Guides.pod 413
lib/Mojolicious/Lite.pm 10768
lib/Mojolicious/Plugin/AgentCondition.pm 660
lib/Mojolicious/Plugin/CallbackCondition.pm 066
lib/Mojolicious/Plugin/Charset.pm 41
lib/Mojolicious/Plugin/Config.pm 55
lib/Mojolicious/Plugin/DefaultHelpers.pm 5935
lib/Mojolicious/Plugin/EpRenderer.pm 48
lib/Mojolicious/Plugin/EplRenderer.pm 54
lib/Mojolicious/Plugin/HeaderCondition.pm 3642
lib/Mojolicious/Plugin/I18n.pm 94
lib/Mojolicious/Plugin/JsonConfig.pm 22
lib/Mojolicious/Plugin/Mount.pm 080
lib/Mojolicious/Plugin/PodRenderer.pm 44
lib/Mojolicious/Plugin/RequestTimer.pm 108
lib/Mojolicious/Plugin/TagHelpers.pm 1522
lib/Mojolicious/Plugins.pm 129
lib/Mojolicious/Renderer.pm 1616
lib/Mojolicious/Routes/Match.pm 2417
lib/Mojolicious/Routes/Pattern.pm 3162
lib/Mojolicious/Routes.pm 6273
lib/Mojolicious/Sessions.pm 51
lib/Mojolicious/Static.pm 2324
lib/Mojolicious/Types.pm 11
lib/Mojolicious/public/css/prettify.css 11
lib/Mojolicious/public/js/lang-apollo.js 22
lib/Mojolicious/public/js/lang-clj.js 018
lib/Mojolicious/public/js/lang-css.js 22
lib/Mojolicious/public/js/lang-go.js 01
lib/Mojolicious/public/js/lang-hs.js 22
lib/Mojolicious/public/js/lang-lisp.js 23
lib/Mojolicious/public/js/lang-lua.js 22
lib/Mojolicious/public/js/lang-ml.js 22
lib/Mojolicious/public/js/lang-n.js 04
lib/Mojolicious/public/js/lang-proto.js 11
lib/Mojolicious/public/js/lang-scala.js 22
lib/Mojolicious/public/js/lang-sql.js 22
lib/Mojolicious/public/js/lang-tex.js 01
lib/Mojolicious/public/js/lang-vb.js 22
lib/Mojolicious/public/js/lang-vhdl.js 33
lib/Mojolicious/public/js/lang-wiki.js 22
lib/Mojolicious/public/js/lang-xq.js 03
lib/Mojolicious/public/js/lang-yaml.js 22
lib/Mojolicious/public/js/prettify.js 3328
lib/Mojolicious/templates/exception.development.html.ep 2424
lib/Mojolicious/templates/perldoc.html.ep 22
lib/Mojolicious.pm 6166
lib/Test/Mojo.pm 3028
lib/ojo.pm 11
script/hypnotoad 12
script/mojo 11
script/morbo 079
t/mojo/bytestream.t 718
t/mojo/command.t 147
t/mojo/cookiejar.t 1224
t/mojo/date.t 22
t/mojo/dom.t 120
t/mojo/hypnotoad.t 31
t/mojo/ioloop.t 543
t/mojo/ioloop_online.t 1770
t/mojo/loader.t 281
t/mojo/message.t 33
t/mojo/morbo.t 0153
t/mojo/parameters.t 130
t/mojo/resolver_online.t 0189
t/mojo/template.t 2832
t/mojo/url.t 1112
t/mojo/user_agent.t 313
t/mojo/user_agent_online.t 11
t/mojolicious/app.t 64
t/mojolicious/dispatch.t 44
t/mojolicious/embedded_lite_app.t 4111
t/mojolicious/exception_lite_app.t 920
t/mojolicious/external/myapp.pl 235
t/mojolicious/external/templates/index.html.ep 02
t/mojolicious/external_lite_app.t 212
t/mojolicious/lib/MojoliciousTest/Exceptional.pm 11
t/mojolicious/lib/MojoliciousTest/Foo.pm 11
t/mojolicious/lite_app.t 27190
t/mojolicious/ojo.t 54
t/mojolicious/pattern.t 44
t/mojolicious/production_app.t 64
t/mojolicious/routes.t 1146
t/mojolicious/tag_helper_lite_app.t 459
t/mojolicious/testing_app.t 64
162 files changed (This is a version diff) 35445201
@@ -1,5 +1,83 @@
 This file documents the revision history for Perl extension Mojolicious.
 
+1.49    2011-06-30 00:00:00
+        - Added EXPERIMENTAL Mojo::IOWatcher and Mojo::Resolver modules,
+          which contain extracted functionality from Mojo::IOLoop.
+        - Added EXPERIMENTAL Mojo::Transactor module, which contains
+          extracted functionality from Mojo::UserAgent.
+        - Added EXPERIMENTAL support for simple alternative placeholder
+          values to routes.
+        - Improved Morbo restarter to also check for changed file size in
+          addition to mtime.
+        - Improved documentation.
+        - Fixed many small route bugs.
+        - Fixed small reparse bug in Mojo::Parameters. (dmw397)
+        - Fixed url_for to incorporate trailing slash for current route.
+        - Fixed a few small error reporting bugs around Mojo::UserAgent.
+        - Foxed portability issue in "command.t".
+
+1.48    2011-06-24 00:00:00
+        - Added debug log message for missing action to router.
+        - Improved Mojo::Command tests.
+        - Improved documentation.
+        - Fixed support for multiple checkboxes with same name in
+          Mojolicious::Plugin::TagHelper. (sri, kimoto)
+        - Fixed portability issue in "lite_app.t". (marcus)
+
+1.47    2011-06-22 00:00:00
+        - Added EXPERIMENTAL callback condition plugin.
+        - Added EXPERIMENTAL host condition to
+          Mojolicious::Plugin::HeaderCondition.
+        - Added host support to Mojolicious::Plugin::Mount. (sri, alnewkirk)
+        - Removed Mojolicious::Plugin::AgentCondition and added its
+          functionality to Mojolicious::Plugin::HeaderCondition.
+        - Improved overall performance slightly.
+        - Improved documentation.
+        - Fixed render_static return value.
+        - Fixed dispatcher return values.
+
+1.46    2011-06-21 00:00:00
+        - Improved Morbo to only attempt restarting applications after a file
+          change has been detected.
+        - Improved overall performance by about 3% with many small
+          optimizations.
+        - Improved query string support in Mojo::Parameters.
+        - Improved tests.
+        - Fixed Windows restart issues in Morbo. (lammel)
+        - Fixed missing plugin error message.
+
+1.45    2011-06-20 00:00:00
+        - Improved documentation.
+        - Fixed an exception with layout rendering bug.
+        - Fixed small Mojo::Parameters bug.
+
+1.44    2011-06-18 00:00:00
+        - Added EXPERIMENTAL self-restarting Morbo development web server and
+          removed old "--reload" support since there have been too many
+          negative side effects.
+          This also improves overall performance by about 5-10% and reduces
+          memory usage by about 10%.
+        - Added EXPERIMENTAL application mount plugin.
+        - Added EXPERIMENTAL --help support for all commands.
+        - Updated prettify.js to version 1-Jun-2011.
+        - Updated WebSocket diagnostics test in Mojo::HelloWorld for latest
+          Firefox Aurora.
+        - Improved inline template and static file performance.
+        - Improved whitespace trimming in Mojo::DOM. (sri, DaTa)
+        - Improved documentation.
+        - Improved tests.
+        - Fixed Morbo file discovery bug. (crab)
+        - Fixed a few application embedding bugs.
+        - Fixed link generation bug in Mojolicious::Plugin::PodRenderer.
+        - Fixed small embedding bug in Mojolicious::Plugin::RequestTimer.
+
+1.43    2011-06-13 00:00:00
+        - Improved after_dispatch hook by allowing it to change session data.
+        - Improved documentation.
+        - Fixed a bug in Mojo::Template that caused template blocks to be
+          auto escaped.
+        - Fixed typos.
+
 1.42    2011-06-09 00:00:00
         - Added EXPERIMENTAL support for unquoted wildcard placeholders in
           Mojolicious::Routes::Pattern.
@@ -215,7 +293,7 @@ This file documents the revision history for Perl extension Mojolicious.
         - Added more Perl-ish configuration plugin.
         - Added drain callback support for WebSockets.
         - Added line numbers to Mojo::JSON error messages. (marcus)
-        - Removed experimental status from hypnotoad and
+        - Removed experimental status from Hypnotoad and
           Mojolicious::Plugin::TagHelpers.
         - Removed experimental status from many attributes and methods all
           over Mojolicious.
@@ -238,7 +316,7 @@ This file documents the revision history for Perl extension Mojolicious.
         - Fixed Mojo::Client async mode.
         - Fixed session cookie encoding bug.
         - Fixed small route bug.
-        - Fixed small hypnotoad bug. (bduggan)
+        - Fixed small Hypnotoad bug. (bduggan)
 
 1.1     2011-02-14 00:00:00
         - Code name "Smiling Cat Face With Heart-Shaped Eyes", this is a
@@ -498,7 +576,7 @@ This file documents the revision history for Perl extension Mojolicious.
           Mojolicious::Plugin::JsonConfig. (marcus)
         - Added reserved route name current.
         - Simplified transaction pausing by replacing it with an automatism.
-        - Improved RFC3986 compliance of Mojo::Path. (janus)
+        - Improved RFC 3986 compliance of Mojo::Path. (janus)
         - Improved Mojo::Server::PSGI to preload applications.
         - Improved FastCGI detection for Dreamhost. (garu)
         - Improved keep alive timeout handling in Mojo::Client.
@@ -1195,7 +1273,7 @@ This file documents the revision history for Perl extension Mojolicious.
         - Updated Mojo::Server::FastCGI and Mojo::Server::Daemon::Prefork to
           use the application logger.
         - Fixed import problem in Mojo::Server::Daemon::Prefork. (James Duncan)
-        - Fixed warning in template.t.
+        - Fixed warning in "template.t".
 
 0.991238 2009-07-16 00:00:00
         - Fixed all shebang lines.
@@ -1226,7 +1304,7 @@ This file documents the revision history for Perl extension Mojolicious.
         - Simplified Mojo::Home.
         - Moved executable detection to Test::Mojo::Server.
         - Improved Mojo::Loader::Exception.
-        - Moved persistent_error.t tests to app.t.
+        - Moved "persistent_error.t" tests to "app.t".
         - Cleaned up code.
         - Fixed at_least_version. (yuki-kimoto)
 
@@ -1394,7 +1472,7 @@ This file documents the revision history for Perl extension Mojolicious.
 0.8007  2008-11-07 00:00:00
         - Cleaned up the api some more.
         - Added param to Mojo::Message.
-        - Added server.t. (Mark Stosberg)
+        - Added "server.t". (Mark Stosberg)
         - Added documentation. (Mark Stosberg)
         - Cleaned up Mojo::File api.
         - Fixed infinite loop in Mojo::File. (Leon Brocard)
@@ -1433,7 +1511,7 @@ This file documents the revision history for Perl extension Mojolicious.
         - Removed OS X resource fork files.
 
 0.8.1   2008-11-01 00:00:00
-        - Made daemon.t developer only.
+        - Made "daemon.t" developer only.
         - Fixed typos.
 
 0.8     2008-10-21 00:00:00
@@ -25,6 +25,9 @@ lib/Mojo/Headers.pm
 lib/Mojo/HelloWorld.pm
 lib/Mojo/Home.pm
 lib/Mojo/IOLoop.pm
+lib/Mojo/IOWatcher.pm
+lib/Mojo/IOWatcher/Epoll.pm
+lib/Mojo/IOWatcher/KQueue.pm
 lib/Mojo/JSON.pm
 lib/Mojo/Loader.pm
 lib/Mojo/Log.pm
@@ -33,16 +36,19 @@ lib/Mojo/Message/Request.pm
 lib/Mojo/Message/Response.pm
 lib/Mojo/Parameters.pm
 lib/Mojo/Path.pm
+lib/Mojo/Resolver.pm
 lib/Mojo/Server.pm
 lib/Mojo/Server/CGI.pm
 lib/Mojo/Server/Daemon.pm
 lib/Mojo/Server/FastCGI.pm
 lib/Mojo/Server/Hypnotoad.pm
+lib/Mojo/Server/Morbo.pm
 lib/Mojo/Server/PSGI.pm
 lib/Mojo/Template.pm
 lib/Mojo/Transaction.pm
 lib/Mojo/Transaction/HTTP.pm
 lib/Mojo/Transaction/WebSocket.pm
+lib/Mojo/Transactor.pm
 lib/Mojo/Upload.pm
 lib/Mojo/URL.pm
 lib/Mojo/UserAgent.pm
@@ -76,7 +82,7 @@ lib/Mojolicious/Guides/Rendering.pod
 lib/Mojolicious/Guides/Routing.pod
 lib/Mojolicious/Lite.pm
 lib/Mojolicious/Plugin.pm
-lib/Mojolicious/Plugin/AgentCondition.pm
+lib/Mojolicious/Plugin/CallbackCondition.pm
 lib/Mojolicious/Plugin/Charset.pm
 lib/Mojolicious/Plugin/Config.pm
 lib/Mojolicious/Plugin/DefaultHelpers.pm
@@ -85,6 +91,7 @@ lib/Mojolicious/Plugin/EpRenderer.pm
 lib/Mojolicious/Plugin/HeaderCondition.pm
 lib/Mojolicious/Plugin/I18n.pm
 lib/Mojolicious/Plugin/JsonConfig.pm
+lib/Mojolicious/Plugin/Mount.pm
 lib/Mojolicious/Plugin/PodRenderer.pm
 lib/Mojolicious/Plugin/PoweredBy.pm
 lib/Mojolicious/Plugin/RequestTimer.pm
@@ -97,17 +104,22 @@ lib/Mojolicious/public/failraptor.png
 lib/Mojolicious/public/favicon.ico
 lib/Mojolicious/public/js/jquery.js
 lib/Mojolicious/public/js/lang-apollo.js
+lib/Mojolicious/public/js/lang-clj.js
 lib/Mojolicious/public/js/lang-css.js
+lib/Mojolicious/public/js/lang-go.js
 lib/Mojolicious/public/js/lang-hs.js
 lib/Mojolicious/public/js/lang-lisp.js
 lib/Mojolicious/public/js/lang-lua.js
 lib/Mojolicious/public/js/lang-ml.js
+lib/Mojolicious/public/js/lang-n.js
 lib/Mojolicious/public/js/lang-proto.js
 lib/Mojolicious/public/js/lang-scala.js
 lib/Mojolicious/public/js/lang-sql.js
+lib/Mojolicious/public/js/lang-tex.js
 lib/Mojolicious/public/js/lang-vb.js
 lib/Mojolicious/public/js/lang-vhdl.js
 lib/Mojolicious/public/js/lang-wiki.js
+lib/Mojolicious/public/js/lang-xq.js
 lib/Mojolicious/public/js/lang-yaml.js
 lib/Mojolicious/public/js/prettify.js
 lib/Mojolicious/public/mojolicious-arrow.png
@@ -136,11 +148,13 @@ lib/Test/Mojo.pm
 LICENSE
 Makefile.PL
 MANIFEST			This list of files
+MANIFEST.bak
 MANIFEST.SKIP
 MYMETA.yml
 README.pod
 script/hypnotoad
 script/mojo
+script/morbo
 t/mojo/apache_cgi.t
 t/mojo/apache_fastcgi.t
 t/mojo/app.t
@@ -168,7 +182,6 @@ t/mojo/headers.t
 t/mojo/home.t
 t/mojo/hypnotoad.t
 t/mojo/ioloop.t
-t/mojo/ioloop_online.t
 t/mojo/ioloop_tls.t
 t/mojo/json.t
 t/mojo/lib/BaseTest/Base1.pm
@@ -184,9 +197,11 @@ t/mojo/lib/test.mt
 t/mojo/lib/utf8_exception.mt
 t/mojo/loader.t
 t/mojo/message.t
+t/mojo/morbo.t
 t/mojo/parameters.t
 t/mojo/path.t
 t/mojo/psgi.t
+t/mojo/resolver_online.t
 t/mojo/server.t
 t/mojo/template.t
 t/mojo/url.t
@@ -203,6 +218,7 @@ t/mojolicious/external/myapp.conf
 t/mojolicious/external/myapp.pl
 t/mojolicious/external/myapp.testing.conf
 t/mojolicious/external/public/index.html
+t/mojolicious/external/templates/index.html.ep
 t/mojolicious/external_lite_app.t
 t/mojolicious/i18n_lite_app.t
 t/mojolicious/i18n_shortcut_lite_app.t
@@ -0,0 +1,276 @@
+.perltidyrc
+Changes
+examples/connect-proxy.pl
+examples/flash-policy-server.pl
+examples/microhttpd.pl
+lib/Mojo.pm
+lib/Mojo/Asset.pm
+lib/Mojo/Asset/File.pm
+lib/Mojo/Asset/Memory.pm
+lib/Mojo/Base.pm
+lib/Mojo/ByteStream.pm
+lib/Mojo/Cache.pm
+lib/Mojo/Command.pm
+lib/Mojo/Content.pm
+lib/Mojo/Content/MultiPart.pm
+lib/Mojo/Content/Single.pm
+lib/Mojo/Cookie.pm
+lib/Mojo/Cookie/Request.pm
+lib/Mojo/Cookie/Response.pm
+lib/Mojo/CookieJar.pm
+lib/Mojo/Date.pm
+lib/Mojo/DOM.pm
+lib/Mojo/Exception.pm
+lib/Mojo/Headers.pm
+lib/Mojo/HelloWorld.pm
+lib/Mojo/Home.pm
+lib/Mojo/IOLoop.pm
+lib/Mojo/JSON.pm
+lib/Mojo/Loader.pm
+lib/Mojo/Log.pm
+lib/Mojo/Message.pm
+lib/Mojo/Message/Request.pm
+lib/Mojo/Message/Response.pm
+lib/Mojo/Parameters.pm
+lib/Mojo/Path.pm
+lib/Mojo/Server.pm
+lib/Mojo/Server/CGI.pm
+lib/Mojo/Server/Daemon.pm
+lib/Mojo/Server/FastCGI.pm
+lib/Mojo/Server/Hypnotoad.pm
+lib/Mojo/Server/Morbo.pm
+lib/Mojo/Server/PSGI.pm
+lib/Mojo/Template.pm
+lib/Mojo/Transaction.pm
+lib/Mojo/Transaction/HTTP.pm
+lib/Mojo/Transaction/WebSocket.pm
+lib/Mojo/Upload.pm
+lib/Mojo/URL.pm
+lib/Mojo/UserAgent.pm
+lib/Mojo/Util.pm
+lib/Mojolicious.pm
+lib/Mojolicious/Command/Cgi.pm
+lib/Mojolicious/Command/Daemon.pm
+lib/Mojolicious/Command/Eval.pm
+lib/Mojolicious/Command/Fastcgi.pm
+lib/Mojolicious/Command/Generate.pm
+lib/Mojolicious/Command/Generate/App.pm
+lib/Mojolicious/Command/Generate/Gitignore.pm
+lib/Mojolicious/Command/Generate/Hypnotoad.pm
+lib/Mojolicious/Command/Generate/LiteApp.pm
+lib/Mojolicious/Command/Generate/Makefile.pm
+lib/Mojolicious/Command/Get.pm
+lib/Mojolicious/Command/Inflate.pm
+lib/Mojolicious/Command/Psgi.pm
+lib/Mojolicious/Command/Routes.pm
+lib/Mojolicious/Command/Test.pm
+lib/Mojolicious/Command/Version.pm
+lib/Mojolicious/Commands.pm
+lib/Mojolicious/Controller.pm
+lib/Mojolicious/Guides.pod
+lib/Mojolicious/Guides/Cheatsheet.pod
+lib/Mojolicious/Guides/CodingGuidelines.pod
+lib/Mojolicious/Guides/Cookbook.pod
+lib/Mojolicious/Guides/FAQ.pod
+lib/Mojolicious/Guides/Growing.pod
+lib/Mojolicious/Guides/Rendering.pod
+lib/Mojolicious/Guides/Routing.pod
+lib/Mojolicious/Lite.pm
+lib/Mojolicious/Plugin.pm
+lib/Mojolicious/Plugin/CallbackCondition.pm
+lib/Mojolicious/Plugin/Charset.pm
+lib/Mojolicious/Plugin/Config.pm
+lib/Mojolicious/Plugin/DefaultHelpers.pm
+lib/Mojolicious/Plugin/EplRenderer.pm
+lib/Mojolicious/Plugin/EpRenderer.pm
+lib/Mojolicious/Plugin/HeaderCondition.pm
+lib/Mojolicious/Plugin/I18n.pm
+lib/Mojolicious/Plugin/JsonConfig.pm
+lib/Mojolicious/Plugin/Mount.pm
+lib/Mojolicious/Plugin/PodRenderer.pm
+lib/Mojolicious/Plugin/PoweredBy.pm
+lib/Mojolicious/Plugin/RequestTimer.pm
+lib/Mojolicious/Plugin/TagHelpers.pm
+lib/Mojolicious/Plugins.pm
+lib/Mojolicious/public/amelia.png
+lib/Mojolicious/public/css/prettify-mojo.css
+lib/Mojolicious/public/css/prettify.css
+lib/Mojolicious/public/failraptor.png
+lib/Mojolicious/public/favicon.ico
+lib/Mojolicious/public/js/jquery.js
+lib/Mojolicious/public/js/lang-apollo.js
+lib/Mojolicious/public/js/lang-clj.js
+lib/Mojolicious/public/js/lang-css.js
+lib/Mojolicious/public/js/lang-go.js
+lib/Mojolicious/public/js/lang-hs.js
+lib/Mojolicious/public/js/lang-lisp.js
+lib/Mojolicious/public/js/lang-lua.js
+lib/Mojolicious/public/js/lang-ml.js
+lib/Mojolicious/public/js/lang-n.js
+lib/Mojolicious/public/js/lang-proto.js
+lib/Mojolicious/public/js/lang-scala.js
+lib/Mojolicious/public/js/lang-sql.js
+lib/Mojolicious/public/js/lang-tex.js
+lib/Mojolicious/public/js/lang-vb.js
+lib/Mojolicious/public/js/lang-vhdl.js
+lib/Mojolicious/public/js/lang-wiki.js
+lib/Mojolicious/public/js/lang-xq.js
+lib/Mojolicious/public/js/lang-yaml.js
+lib/Mojolicious/public/js/prettify.js
+lib/Mojolicious/public/mojolicious-arrow.png
+lib/Mojolicious/public/mojolicious-black.png
+lib/Mojolicious/public/mojolicious-box.png
+lib/Mojolicious/public/mojolicious-clouds.png
+lib/Mojolicious/public/mojolicious-noraptor.png
+lib/Mojolicious/public/mojolicious-notfound.png
+lib/Mojolicious/public/mojolicious-pinstripe.gif
+lib/Mojolicious/public/mojolicious-white.png
+lib/Mojolicious/Renderer.pm
+lib/Mojolicious/Routes.pm
+lib/Mojolicious/Routes/Match.pm
+lib/Mojolicious/Routes/Pattern.pm
+lib/Mojolicious/Sessions.pm
+lib/Mojolicious/Static.pm
+lib/Mojolicious/templates/exception.development.html.ep
+lib/Mojolicious/templates/exception.html.ep
+lib/Mojolicious/templates/mojobar.html.ep
+lib/Mojolicious/templates/not_found.development.html.ep
+lib/Mojolicious/templates/not_found.html.ep
+lib/Mojolicious/templates/perldoc.html.ep
+lib/Mojolicious/Types.pm
+lib/ojo.pm
+lib/Test/Mojo.pm
+LICENSE
+Makefile.PL
+MANIFEST			This list of files
+MANIFEST.SKIP
+MYMETA.yml
+README.pod
+script/hypnotoad
+script/mojo
+script/morbo
+t/mojo/apache_cgi.t
+t/mojo/apache_fastcgi.t
+t/mojo/app.t
+t/mojo/asset.t
+t/mojo/base.t
+t/mojo/bytestream.t
+t/mojo/cache.t
+t/mojo/certs/badclient.crt
+t/mojo/certs/badclient.key
+t/mojo/certs/ca.crt
+t/mojo/certs/ca.key
+t/mojo/certs/client.crt
+t/mojo/certs/client.key
+t/mojo/certs/server.crt
+t/mojo/certs/server.key
+t/mojo/cgi.t
+t/mojo/command.t
+t/mojo/content.t
+t/mojo/cookie.t
+t/mojo/cookiejar.t
+t/mojo/date.t
+t/mojo/dom.t
+t/mojo/fastcgi.t
+t/mojo/headers.t
+t/mojo/home.t
+t/mojo/hypnotoad.t
+t/mojo/ioloop.t
+t/mojo/ioloop_online.t
+t/mojo/ioloop_tls.t
+t/mojo/json.t
+t/mojo/lib/BaseTest/Base1.pm
+t/mojo/lib/BaseTest/Base2.pm
+t/mojo/lib/BaseTest/Base3.pm
+t/mojo/lib/exception.mt
+t/mojo/lib/LoaderException.pm
+t/mojo/lib/LoaderException2.pm
+t/mojo/lib/LoaderTest/A.pm
+t/mojo/lib/LoaderTest/B.pm
+t/mojo/lib/LoaderTest/C.pm
+t/mojo/lib/test.mt
+t/mojo/lib/utf8_exception.mt
+t/mojo/loader.t
+t/mojo/message.t
+t/mojo/morbo.t
+t/mojo/parameters.t
+t/mojo/path.t
+t/mojo/psgi.t
+t/mojo/server.t
+t/mojo/template.t
+t/mojo/url.t
+t/mojo/user_agent.t
+t/mojo/user_agent_online.t
+t/mojo/user_agent_tls.t
+t/mojolicious/app.t
+t/mojolicious/charset_lite_app.t
+t/mojolicious/dispatch.t
+t/mojolicious/dispatcher_lite_app.t
+t/mojolicious/embedded_lite_app.t
+t/mojolicious/exception_lite_app.t
+t/mojolicious/external/myapp.conf
+t/mojolicious/external/myapp.pl
+t/mojolicious/external/myapp.testing.conf
+t/mojolicious/external/public/index.html
+t/mojolicious/external/templates/index.html.ep
+t/mojolicious/external_lite_app.t
+t/mojolicious/i18n_lite_app.t
+t/mojolicious/i18n_shortcut_lite_app.t
+t/mojolicious/json_config_lite_app.json
+t/mojolicious/json_config_lite_app.t
+t/mojolicious/json_config_mode_lite_app.json
+t/mojolicious/json_config_mode_lite_app.t
+t/mojolicious/json_config_mode_lite_app.testing.json
+t/mojolicious/layouted_lite_app.t
+t/mojolicious/lib/EmbeddedTestApp.pm
+t/mojolicious/lib/MojoliciousTest.pm
+t/mojolicious/lib/MojoliciousTest/Exceptional.pm
+t/mojolicious/lib/MojoliciousTest/Foo.pm
+t/mojolicious/lib/MojoliciousTest/Foo/Bar.pm
+t/mojolicious/lib/MojoliciousTest/Plugin/TestPlugin.pm
+t/mojolicious/lib/MojoliciousTest/SyntaxError.pm
+t/mojolicious/lib/MojoliciousTest2/Foo.pm
+t/mojolicious/lib/MojoliciousTestController.pm
+t/mojolicious/lib/PluginWithEmbeddedApp.pm
+t/mojolicious/lib/PluginWithTemplate.pm
+t/mojolicious/lib/SingleFileTestApp.pm
+t/mojolicious/lite_app.t
+t/mojolicious/longpolling_lite_app.t
+t/mojolicious/ojo.t
+t/mojolicious/pattern.t
+t/mojolicious/pod_renderer_lite_app.t
+t/mojolicious/production_app.t
+t/mojolicious/public/hello.txt
+t/mojolicious/public/hello2.txt
+t/mojolicious/public_dev/hello.txt
+t/mojolicious/rebased_lite_app.t
+t/mojolicious/renderer.t
+t/mojolicious/routes.t
+t/mojolicious/secret.txt
+t/mojolicious/tag_helper_lite_app.t
+t/mojolicious/templates/23.html.epl
+t/mojolicious/templates/dies_too.html.ep
+t/mojolicious/templates/encoding.koi8-r.ep
+t/mojolicious/templates/exception.html.epl
+t/mojolicious/templates/exception.testing.html.ep
+t/mojolicious/templates/foo/bar.rss.ep
+t/mojolicious/templates/foo/bar/index.html.epl
+t/mojolicious/templates/foo/index.html.xpl
+t/mojolicious/templates/foo/yada.html.ep
+t/mojolicious/templates/layouts/default.html.epl
+t/mojolicious/templates/layouts/green.html.epl
+t/mojolicious/templates/not_found.testing.html.ep
+t/mojolicious/templates/simple.html.pod
+t/mojolicious/templates/syntaxerror.html.epl
+t/mojolicious/templates/withblock.txt.epl
+t/mojolicious/templates/withlayout.html.epl
+t/mojolicious/testing_app.t
+t/mojolicious/tls_lite_app.t
+t/mojolicious/twinkle_lite_app.conf
+t/mojolicious/twinkle_lite_app.t
+t/mojolicious/upload_lite_app.t
+t/mojolicious/websocket_lite_app.t
+t/mojolicious/websocket_proxy_lite_app.t
+t/mojolicious/websocket_tls_proxy_lite_app.t
+t/pod.t
+t/pod_coverage.t
@@ -1,6 +1,6 @@
 --- #YAML:1.0
 name:               Mojolicious
-version:            1.42
+version:            1.49
 abstract:           The Web In A Box!
 author:
     - Sebastian Riedel <sri@cpan.org>
@@ -57,4 +57,4 @@ resources:
   homepage: http://mojolicio.us
   license: http://dev.perl.org/licenses/
   repository: http://github.com/kraih/mojo
-version: 1.42
+version: 1.49
@@ -42,7 +42,7 @@ WriteMakefile(
     )
   ),
 
-  EXE_FILES => ['script/mojo', 'script/hypnotoad'],
+  EXE_FILES => ['script/hypnotoad', 'script/mojo', 'script/morbo'],
   PREREQ_PM => {
     'B'                     => 0,
     'Carp'                  => 0,
@@ -38,12 +38,12 @@ TLS, Bonjour, IDNA, Comet (long polling), chunking and multipart support.
 
 =item *
 
-Builtin async IO web server supporting epoll, kqueue, UNIX domain sockets and
-hot deployment, perfect for embedding.
+Built-in async IO web server supporting epoll, kqueue, UNIX domain sockets
+and hot deployment, perfect for embedding.
 
 =item *
 
-Automatic CGI, FastCGI, and L<PSGI> detection.
+Automatic CGI, FastCGI and L<PSGI> detection.
 
 =item *
 
@@ -57,7 +57,7 @@ Fresh code based upon years of experience developing L<Catalyst>.
 
 =head2 Installation
 
-All you need is a oneliner.
+All you need is a oneliner, it takes less than a minute.
 
   sudo sh -c "curl -L cpanmin.us | perl - Mojolicious"
 
@@ -67,12 +67,12 @@ These three lines are a whole web application.
 
   use Mojolicious::Lite;
 
-  get '/' => sub { shift->render_text('Hello World!') };
+  get '/' => {text => 'Hello World!'};
 
   app->start;
 
-To run this example with the built-in development server just put the code
-into a file and execute it with C<perl>.
+To run this example with the built-in development web server just put the
+code into a file and execute it with C<perl>.
 
   % perl hello.pl daemon
   Server available at http://127.0.0.1:3000.
@@ -87,16 +87,18 @@ Web development for humans, making hard things possible and everything fun.
   use Mojolicious::Lite;
 
   # Simple plain text response
-  get '/' => sub { shift->render_text('Hello World!') };
+  get '/' => sub {
+    my $self = shift;
+    $self->render_text('Hello World!');
+  };
 
   # Route associating the "/time" URL to template in DATA section
   get '/time' => 'clock';
 
   # RESTful web service sending JSON responses
-  get '/:offset' => sub {
-    my $self   = shift;
-    my $offset = $self->param('offset') || 23;
-    $self->render_json({list => [0 .. $offset]});
+  get '/list/:offset' => sub {
+    my $self = shift;
+    $self->render_json({list => [0 .. $self->param('offset')]});
   };
 
   # Scrape and return information from remote sites
@@ -134,16 +136,18 @@ A controller collects several actions together.
   use Mojo::Base 'Mojolicious::Controller';
 
   # Plain text response
-  sub hello { shift->render_text('Hello World!') }
+  sub hello {
+    my $self = shift;
+    $self->render_text('Hello World!');
+  }
 
   # Render external template "templates/example/clock.html.ep"
-  sub clock { shift->render }
+  sub clock { }
 
   # RESTful web service sending JSON responses
   sub restful {
-    my $self   = shift;
-    my $offset = $self->param('offset') || 23;
-    $self->render_json({list => [0 .. $offset]});
+    my $self = shift;
+    $self->render_json({list => [0 .. $self->param('offset')]});
   }
 
   # Scrape and return information from remote sites
@@ -191,7 +195,7 @@ especially when working in a team.
     # (paths are relative to the controller)
     $example->get('/')->to('#hello');
     $example->get('/time')->to('#clock');
-    $example->get('/:offset')->to('#restful');
+    $example->get('/list/:offset')->to('#restful');
 
     # All common HTTP verbs are supported
     $example->post('/title')->to('#title');
@@ -15,11 +15,9 @@ has [qw/cleanup path/];
 has handle => sub {
   my $self = shift;
 
-  # Handle
-  my $handle = IO::File->new;
-
   # Already got a file without handle
-  my $file = $self->path;
+  my $handle = IO::File->new;
+  my $file   = $self->path;
   if ($file && -f $file) {
     $handle->open("< $file")
       or croak qq/Can't open file "$file": $!/;
@@ -43,7 +41,7 @@ has handle => sub {
   # Open for read/write access
   $handle->fdopen(fileno($fh), "+>") or croak qq/Can't open file "$name": $!/;
 
-  return $handle;
+  $handle;
 };
 has tmpdir => sub { $ENV{MOJO_TMPDIR} || File::Spec->tmpdir };
 
@@ -53,8 +51,6 @@ has tmpdir => sub { $ENV{MOJO_TMPDIR} || File::Spec->tmpdir };
 #  claws!"
 sub DESTROY {
   my $self = shift;
-
-  # Cleanup
   my $path = $self->path;
   if ($self->cleanup && -f $path) {
     close $self->handle;
@@ -73,7 +69,7 @@ sub add_chunk {
   utf8::encode $chunk if utf8::is_utf8 $chunk;
   $self->handle->syswrite($chunk, length $chunk);
 
-  return $self;
+  $self;
 }
 
 sub contains {
@@ -107,7 +103,7 @@ sub contains {
     substr $window, 0, $read, '';
   }
 
-  return -1;
+  -1;
 }
 
 sub get_chunk {
@@ -131,7 +127,7 @@ sub get_chunk {
   }
   else { $self->handle->sysread($buffer, $size) }
 
-  return $buffer;
+  $buffer;
 }
 
 sub move_to {
@@ -150,7 +146,7 @@ sub move_to {
   # Don't clean up a moved file
   $self->cleanup(0);
 
-  return $self;
+  $self;
 }
 
 sub size {
@@ -160,7 +156,7 @@ sub size {
   my $file = $self->path;
   return -s $file if $file;
 
-  return 0;
+  0;
 }
 
 sub slurp {
@@ -173,7 +169,7 @@ sub slurp {
   my $content = '';
   while ($self->handle->sysread(my $buffer, 131072)) { $content .= $buffer }
 
-  return $content;
+  $content;
 }
 
 1;
@@ -10,14 +10,14 @@ use IO::File;
 sub new {
   my $self = shift->SUPER::new(@_);
   $self->{content} = '';
-  return $self;
+  $self;
 }
 
 sub add_chunk {
   my ($self, $chunk) = @_;
   utf8::encode $chunk if utf8::is_utf8 $chunk;
   $self->{content} .= $chunk if defined $chunk;
-  return $self;
+  $self;
 }
 
 sub contains {
@@ -29,7 +29,7 @@ sub contains {
   my $end = $self->end_range;
 
   return -1 if $end && $pos >= $end;
-  return $pos;
+  $pos;
 }
 
 sub get_chunk {
@@ -41,18 +41,15 @@ sub get_chunk {
     $size = $end + 1 - $start if ($start + $size) > $end;
   }
 
-  return substr shift->{content}, $start, $size;
+  substr shift->{content}, $start, $size;
 }
 
 sub move_to {
   my ($self, $path) = @_;
-
-  # Write
   my $file = IO::File->new;
   $file->open("> $path") or croak qq/Can't open file "$path": $!/;
   $file->syswrite($self->{content});
-
-  return $self;
+  $self;
 }
 
 sub size { length shift->{content} }
@@ -13,13 +13,8 @@ require Carp;
 #  The lesson is, never try."
 sub import {
   my $class = shift;
-
-  # Flag
   return unless my $flag = shift;
 
-  # Caller
-  my $caller = caller;
-
   # No limits!
   no strict 'refs';
   no warnings 'redefine';
@@ -35,6 +30,7 @@ sub import {
   }
 
   # ISA
+  my $caller = caller;
   push @{"${caller}::ISA"}, $flag;
 
   # Can haz?
@@ -50,9 +46,7 @@ sub import {
 
 sub new {
   my $class = shift;
-
-  # Instantiate
-  return bless
+  bless
     exists $_[0] ? exists $_[1] ? {@_} : {%{$_[0]}} : {},
     ref $class || $class;
 }
@@ -70,9 +64,6 @@ sub attr {
   return unless $class && $attrs;
   $class = ref $class || $class;
 
-  # Allow symbolic references
-  no strict 'refs';
-
   # Check default
   Carp::croak('Default has to be a code reference or constant value')
     if ref $default && ref $default ne 'CODE';
@@ -115,12 +106,13 @@ sub attr {
     $code .= "$ws\$_[0]->{'$attr'} = \$_[1];\n";
 
     # Return invocant
-    $code .= "${ws}return \$_[0];\n";
+    $code .= "${ws}\$_[0];\n";
 
     # Footer
     $code .= '};';
 
     # We compile custom attribute code for speed
+    no strict 'refs';
     no warnings 'redefine';
     *{"${class}::$attr"} = eval $code;
 
@@ -5,14 +5,14 @@ use overload '""' => sub { shift->to_string }, fallback => 1;
 use Mojo::Util;
 
 sub import {
-  return unless @_ > 1;
+  my $class = shift;
+  return unless @_ > 0;
 
   # Alternative constructor
   no strict 'refs';
   no warnings 'redefine';
   my $caller = caller;
-  *{"${caller}::b"} =
-    sub { bless {bytestream => join('', @_)}, 'Mojo::ByteStream' };
+  *{"${caller}::b"} = sub { bless {bytestream => join('', @_)}, $class };
 }
 
 # "Do we have any food that wasn't brutally slaughtered?
@@ -20,36 +20,36 @@ sub import {
 sub new {
   my $self = shift->SUPER::new();
   $self->{bytestream} = join '', @_;
-  return $self;
+  $self;
 }
 
 sub b64_decode {
   my $self = shift;
   $self->{bytestream} = Mojo::Util::b64_decode($self->{bytestream});
-  return $self;
+  $self;
 }
 
 sub b64_encode {
   my $self = shift;
   $self->{bytestream} = Mojo::Util::b64_encode($self->{bytestream}, @_);
-  return $self;
+  $self;
 }
 
 sub camelize {
   my $self = shift;
   Mojo::Util::camelize $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub clone {
   my $self = shift;
-  return $self->new($self->{bytestream});
+  $self->new($self->{bytestream});
 }
 
 sub decamelize {
   my $self = shift;
   Mojo::Util::decamelize $self->{bytestream};
-  return $self;
+  $self;
 }
 
 # "I want to share something with you: The three little sentences that will
@@ -60,61 +60,61 @@ sub decamelize {
 sub decode {
   my $self = shift;
   Mojo::Util::decode shift || 'UTF-8', $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub encode {
   my $self = shift;
   Mojo::Util::encode shift || 'UTF-8', $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub hmac_md5_sum {
   my $self = shift;
   $self->{bytestream} = Mojo::Util::hmac_md5_sum $self->{bytestream}, @_;
-  return $self;
+  $self;
 }
 
 sub hmac_sha1_sum {
   my $self = shift;
   $self->{bytestream} = Mojo::Util::hmac_sha1_sum $self->{bytestream}, @_;
-  return $self;
+  $self;
 }
 
 sub html_escape {
   my $self = shift;
   Mojo::Util::html_escape $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub html_unescape {
   my $self = shift;
   Mojo::Util::html_unescape $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub md5_bytes {
   my $self = shift;
   $self->{bytestream} = Mojo::Util::md5_bytes $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub md5_sum {
   my $self = shift;
   $self->{bytestream} = Mojo::Util::md5_sum $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub punycode_decode {
   my $self = shift;
   Mojo::Util::punycode_decode $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub punycode_encode {
   my $self = shift;
   Mojo::Util::punycode_encode $self->{bytestream};
-  return $self;
+  $self;
 }
 
 # "Old people don't need companionship.
@@ -123,19 +123,19 @@ sub punycode_encode {
 sub qp_decode {
   my $self = shift;
   Mojo::Util::qp_decode $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub qp_encode {
   my $self = shift;
   Mojo::Util::qp_encode $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub quote {
   my $self = shift;
   Mojo::Util::quote $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub say {
@@ -148,13 +148,13 @@ sub say {
 sub sha1_bytes {
   my $self = shift;
   $self->{bytestream} = Mojo::Util::sha1_bytes $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub sha1_sum {
   my $self = shift;
   $self->{bytestream} = Mojo::Util::sha1_sum $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub size { length shift->{bytestream} }
@@ -164,31 +164,31 @@ sub to_string { shift->{bytestream} }
 sub trim {
   my $self = shift;
   Mojo::Util::trim $self->{bytestream}, @_;
-  return $self;
+  $self;
 }
 
 sub unquote {
   my $self = shift;
   Mojo::Util::unquote $self->{bytestream}, @_;
-  return $self;
+  $self;
 }
 
 sub url_escape {
   my $self = shift;
   Mojo::Util::url_escape $self->{bytestream}, @_;
-  return $self;
+  $self;
 }
 
 sub url_unescape {
   my $self = shift;
   Mojo::Util::url_unescape $self->{bytestream};
-  return $self;
+  $self;
 }
 
 sub xml_escape {
   my $self = shift;
   Mojo::Util::xml_escape $self->{bytestream};
-  return $self;
+  $self;
 }
 
 1;
@@ -14,16 +14,15 @@ sub get { (shift->{_cache} || {})->{shift()} }
 sub set {
   my ($self, $key, $value) = @_;
 
+  # Cache with size limit
   my $keys  = $self->max_keys;
   my $cache = $self->{_cache} ||= {};
   my $stack = $self->{_stack} ||= [];
-
-  # Cache with size limit
   delete $cache->{shift @$stack} while @$stack >= $keys;
   push @$stack, $key;
   $cache->{$key} = $value;
 
-  return $self;
+  $self;
 }
 
 1;
@@ -38,7 +38,7 @@ sub chmod_file {
   chmod $mod, $path or croak qq/Can't chmod path "$path": $!/;
   $mod = sprintf '%lo', $mod;
   print "  [chmod] $path $mod\n" unless $self->quiet;
-  return $self;
+  $self;
 }
 
 sub chmod_rel_file {
@@ -50,13 +50,13 @@ sub class_to_file {
   my ($self, $class) = @_;
   $class =~ s/:://g;
   decamelize $class;
-  return $class;
+  $class;
 }
 
 sub class_to_path {
   my ($self, $class) = @_;
   my $path = join '/', split /::/, $class;
-  return "$path.pm";
+  "$path.pm";
 }
 
 sub create_dir {
@@ -71,7 +71,7 @@ sub create_dir {
   # Create
   File::Path::mkpath($path) or croak qq/Can't make directory "$path": $!/;
   print "  [mkdir] $path\n" unless $self->quiet;
-  return $self;
+  $self;
 }
 
 sub create_rel_dir {
@@ -96,31 +96,19 @@ sub detect {
   return 'fastcgi' if !defined $ENV{WINDIR} && !defined $ENV{USER};
 
   # Nothing
-  return;
+  undef;
 }
 
 sub get_all_data {
   my ($self, $class) = @_;
   $class ||= ref $self;
 
-  # Handle
+  # Refresh or use cached data
   my $d = do { no strict 'refs'; \*{"$class\::DATA"} };
-
-  # Refresh
-  if (fileno $d) {
-
-    # Reset
-    seek $d, 0, 0;
-
-    # Slurp
-    $CACHE->{$class} = join '', <$d>;
-
-    # Close
-    close $d;
-  }
-
-  # Content
-  return unless defined(my $content = $CACHE->{$class});
+  return $CACHE->{$class} unless fileno $d;
+  seek $d, 0, 0;
+  my $content = join '', <$d>;
+  close $d;
 
   # Ignore everything before __DATA__ (windows will seek to start of file)
   $content =~ s/^.*\n__DATA__\r?\n/\n/s;
@@ -130,25 +118,23 @@ sub get_all_data {
 
   # Split
   my @data = split /^@@\s+(.+?)\s*\r?\n/m, $content;
-
-  # Remove split garbage
   shift @data;
 
   # Find data
-  my $all = {};
+  my $all = $CACHE->{$class} = {};
   while (@data) {
     my ($name, $content) = splice @data, 0, 2;
     b64_decode $content if $name =~ s/\s*\(\s*base64\s*\)$//;
     $all->{$name} = $content;
   }
 
-  return $all;
+  $all;
 }
 
 sub get_data {
   my ($self, $data, $class) = @_;
   my $all = $self->get_all_data($class);
-  return $all->{$data};
+  $all->{$data};
 }
 
 # "You don’t like your job, you don’t strike.
@@ -162,19 +148,19 @@ sub help {
 sub rel_dir {
   my ($self, $path) = @_;
   my @parts = split /\//, $path;
-  return File::Spec->catdir(Cwd::getcwd(), @parts);
+  File::Spec->catdir(Cwd::getcwd(), @parts);
 }
 
 sub rel_file {
   my ($self, $path) = @_;
   my @parts = split /\//, $path;
-  return File::Spec->catfile(Cwd::getcwd(), @parts);
+  File::Spec->catfile(Cwd::getcwd(), @parts);
 }
 
 sub render_data {
   my $self = shift;
   my $data = shift;
-  return $self->renderer->render($self->get_data($data), @_);
+  $self->renderer->render($self->get_data($data), @_);
 }
 
 sub render_to_file {
@@ -182,7 +168,7 @@ sub render_to_file {
   my $data = shift;
   my $path = shift;
   $self->write_file($path, $self->render_data($data, @_));
-  return $self;
+  $self;
 }
 
 sub render_to_rel_file {
@@ -207,6 +193,7 @@ sub run {
     # Help
     my $help = $name eq 'help' ? 1 : 0;
     $name = shift @args if $help;
+    $help = 1           if $ENV{MOJO_HELP};
 
     # Try all namespaces
     my $module;
@@ -296,24 +283,18 @@ sub run {
   }
   print $self->hint;
 
-  return $self;
+  $self;
 }
 
 sub start {
   my $self = shift;
 
-  # Don't run commands if we are reloading
-  return $self if $ENV{MOJO_COMMANDS_DONE};
-  $ENV{MOJO_COMMANDS_DONE} ||= 1;
-
   # Executable
   $ENV{MOJO_EXE} ||= (caller)[1] if $ENV{MOJO_APP};
 
-  # Arguments
-  my @args = @_ ? @_ : @ARGV;
-
   # Run
-  return ref $self ? $self->run(@args) : $self->new->run(@args);
+  my @args = @_ ? @_ : @ARGV;
+  ref $self ? $self->run(@args) : $self->new->run(@args);
 }
 
 sub write_file {
@@ -333,7 +314,7 @@ sub write_file {
   $file->syswrite($data);
   print "  [write] $path\n" unless $self->quiet;
 
-  return $self;
+  $self;
 }
 
 sub write_rel_file {
@@ -16,8 +16,7 @@ sub body_contains {
     $found += $part->body_contains($chunk);
   }
 
-  # Found
-  return $found ? 1 : 0;
+  $found ? 1 : 0;
 }
 
 sub body_size {
@@ -27,11 +26,8 @@ sub body_size {
   my $content_length = $self->headers->content_length;
   return $content_length if $content_length;
 
-  # Boundary
-  my $boundary = $self->build_boundary;
-
   # Calculate length of whole body
-  my $boundary_length = length($boundary) + 6;
+  my $boundary_length = length($self->build_boundary) + 6;
   my $len             = 0;
   $len += $boundary_length - 2;
   for my $part (@{$self->parts}) {
@@ -46,7 +42,7 @@ sub body_size {
     $len += $boundary_length;
   }
 
-  return $len;
+  $len;
 }
 
 sub build_boundary {
@@ -79,7 +75,7 @@ sub build_boundary {
   my $after  = $2 || '';
   $headers->content_type("$before; boundary=$boundary$after");
 
-  return $boundary;
+  $boundary;
 }
 
 sub get_body_chunk {
@@ -88,12 +84,10 @@ sub get_body_chunk {
   # Body generator
   return $self->generate_body_chunk($offset) if $self->on_read;
 
-  # Multipart
+  # First boundary
   my $boundary        = $self->build_boundary;
   my $boundary_length = length($boundary) + 6;
   my $len             = $boundary_length - 2;
-
-  # First boundary
   return substr "--$boundary\x0d\x0a", $offset if $len > $offset;
 
   # Parts
@@ -142,16 +136,14 @@ sub parse {
   # Parse multipart content
   $self->_parse_multipart;
 
-  return $self;
+  $self;
 }
 
 sub _parse_multipart {
   my $self = shift;
 
-  # Need a boundary
-  my $boundary = $self->is_multipart;
-
   # Parse
+  my $boundary = $self->is_multipart;
   while (1) {
 
     # Done
@@ -193,7 +185,7 @@ sub _parse_multipart_body {
   my $chunk = substr $self->{_b2}, 0, $pos, '';
   $self->parts->[-1] = $self->parts->[-1]->parse($chunk);
   $self->{_multi_state} = 'multipart_boundary';
-  return 1;
+  1;
 }
 
 sub _parse_multipart_boundary {
@@ -218,7 +210,7 @@ sub _parse_multipart_boundary {
     $self->{_state} = $self->{_multi_state} = 'done';
   }
 
-  return;
+  undef;
 }
 
 sub _parse_multipart_preamble {
@@ -235,7 +227,7 @@ sub _parse_multipart_preamble {
   }
 
   # No boundary yet
-  return;
+  undef;
 }
 
 1;
@@ -267,7 +259,8 @@ and implements the following new ones.
 
   my $parts = $content->parts;
 
-Content parts embedded in this multipart content.
+Content parts embedded in this multipart content, usually
+L<Mojo::Content::Single> objects.
 
 =head1 METHODS
 
@@ -302,7 +295,7 @@ Get a chunk of content starting from a specfic position.
 
   $content = $content->parse('Content-Type: multipart/mixed');
 
-Parse content.
+Parse content chunk.
 
 =head1 SEE ALSO
 
@@ -9,18 +9,14 @@ has asset => sub { Mojo::Asset::Memory->new };
 
 sub body_contains {
   my ($self, $chunk) = @_;
-
-  # Found
   return 1 if $self->asset->contains($chunk) >= 0;
-
-  # Not found
-  return 0;
+  0;
 }
 
 sub body_size {
   my $self = shift;
   return ($self->headers->content_length || 0) if $self->on_read;
-  return $self->asset->size;
+  $self->asset->size;
 }
 
 sub get_body_chunk {
@@ -30,7 +26,7 @@ sub get_body_chunk {
   return $self->generate_body_chunk($offset) if $self->on_read;
 
   # Normal content
-  return $self->asset->get_chunk($offset);
+  $self->asset->get_chunk($offset);
 }
 
 sub parse {
@@ -42,9 +38,6 @@ sub parse {
   # Still parsing headers or using a custom body parser
   return $self if ($self->{_state} || '') eq 'headers' || $self->on_read;
 
-  # Headers
-  my $headers = $self->headers;
-
   # Content needs to be upgraded to multipart
   if ($self->is_multipart) {
     return $self if $self->isa('Mojo::Content::MultiPart');
@@ -82,7 +75,7 @@ sub parse {
     $self->{_state} = 'done' if $len <= $self->progress;
   }
 
-  return $self;
+  $self;
 }
 
 1;
@@ -114,7 +107,7 @@ implements the following new ones.
   my $asset = $content->asset;
   $content  = $content->asset(Mojo::Asset::Memory->new);
 
-The actual content.
+The actual content, defaults to a L<Mojo::Asset::Memory> object.
 
 =head1 METHODS
 
@@ -143,7 +136,7 @@ Get a chunk of content starting from a specfic position.
 
   $content = $content->parse("Content-Length: 12\r\n\r\nHello World!");
 
-Parse content.
+Parse content chunk.
 
 =head1 SEE ALSO
 
@@ -20,6 +20,7 @@ sub body_size { croak 'Method "body_size" not implemented by subclass' }
 sub build_body {
   my $self = shift;
 
+  # Concatenate all chunks in memory
   my $body   = '';
   my $offset = 0;
   while (1) {
@@ -36,12 +37,13 @@ sub build_body {
     $body .= $chunk;
   }
 
-  return $body;
+  $body;
 }
 
 sub build_headers {
   my $self = shift;
 
+  # Concatenate all chunks in memory
   my $headers = '';
   my $offset  = 0;
   while (1) {
@@ -58,7 +60,7 @@ sub build_headers {
     $headers .= $chunk;
   }
 
-  return $headers;
+  $headers;
 }
 
 # "Aren't we forgetting the true meaning of Christmas?
@@ -66,11 +68,8 @@ sub build_headers {
 sub generate_body_chunk {
   my ($self, $offset) = @_;
 
-  # Delay
-  my $delay = delete $self->{_delay};
-
   # Callback
-  if (!$delay && !length $self->{_b2}) {
+  if (!delete $self->{_delay} && !length $self->{_b2}) {
     my $cb = delete $self->{_drain};
     $self->$cb($offset) if $cb;
   }
@@ -83,7 +82,7 @@ sub generate_body_chunk {
   # EOF or delay
   return $self->{_eof} ? '' : undef unless length $chunk;
 
-  return $chunk;
+  $chunk;
 }
 
 sub get_body_chunk {
@@ -95,53 +94,45 @@ sub get_header_chunk {
 
   # Normal headers
   my $copy = $self->{_b1} ||= $self->_build_headers;
-  return substr($copy, $offset, CHUNK_SIZE);
+  substr($copy, $offset, CHUNK_SIZE);
 }
 
 sub has_leftovers {
   my $self = shift;
-
-  # Leftovers
   return 1 if length $self->{_b2} || length $self->{_b1};
-
-  # Empty buffer
-  return;
+  undef;
 }
 
 sub header_size { length shift->build_headers }
 
 sub is_chunked {
   my $self = shift;
-
-  # Chunked
   my $encoding = $self->headers->transfer_encoding || '';
-  return $encoding =~ /chunked/i ? 1 : 0;
+  $encoding =~ /chunked/i ? 1 : 0;
 }
 
 sub is_done {
   return 1 if (shift->{_state} || '') eq 'done';
-  return;
+  undef;
 }
 
 sub is_dynamic {
   my $self = shift;
   return 1 if $self->on_read && !defined $self->headers->content_length;
-  return;
+  undef;
 }
 
 sub is_multipart {
   my $self = shift;
-
-  # Multipart
   my $type = $self->headers->content_type || '';
   $type =~ /multipart.*boundary=\"*([a-zA-Z0-9\'\(\)\,\.\:\?\-\_\+\/]+)/i
     and return $1;
-  return;
+  undef;
 }
 
 sub is_parsing_body {
   return 1 if (shift->{_state} || '') eq 'body';
-  return;
+  undef;
 }
 
 sub leftovers {
@@ -152,7 +143,7 @@ sub leftovers {
   return $self->{_b1} if length $self->{_b1};
 
   # Normal leftovers
-  return $self->{_b2};
+  $self->{_b2};
 }
 
 sub parse {
@@ -202,7 +193,7 @@ sub parse {
     # Normal content
     else {
 
-      # Need
+      # Bytes needed
       my $len = $self->headers->content_length || 0;
       $self->{_size} ||= 0;
       my $need = $len - $self->{_size};
@@ -219,7 +210,7 @@ sub parse {
     }
   }
 
-  return $self;
+  $self;
 }
 
 sub parse_body {
@@ -232,7 +223,7 @@ sub parse_body_once {
   my $self = shift;
   $self->parse_body(@_);
   $self->{_state} = 'done';
-  return $self;
+  $self;
 }
 
 # "Quick Smithers. Bring the mind eraser device!
@@ -264,7 +255,7 @@ sub parse_until_body {
   # Parse headers
   $self->_parse_headers if ($self->{_state} || '') eq 'headers';
 
-  return $self;
+  $self;
 }
 
 sub progress {
@@ -326,19 +317,14 @@ sub _build_chunk {
     $formatted .= sprintf('%x', length $chunk) . "\x0d\x0a$chunk";
   }
 
-  return $formatted;
+  $formatted;
 }
 
 sub _build_headers {
-  my $self = shift;
-
-  # Build
+  my $self    = shift;
   my $headers = $self->headers->to_string;
-
-  # Empty
   return "\x0d\x0a" unless $headers;
-
-  return "$headers\x0d\x0a\x0d\x0a";
+  "$headers\x0d\x0a\x0d\x0a";
 }
 
 sub _parse_chunked {
@@ -457,14 +443,14 @@ Try to detect broken web servers and turn on relaxed parsing automatically.
   my $headers = $content->headers;
   $content    = $content->headers(Mojo::Headers->new);
 
-The headers.
+Content headers, defaults to a L<Mojo::Headers> object.
 
 =head2 C<on_read>
 
   my $cb   = $content->on_read;
   $content = $content->on_read(sub {...});
 
-Content parser callback.
+Callback to be invoked when new content arrives.
 
   $content = $content->on_read(sub {
     my ($self, $chunk) = @_;
@@ -541,7 +527,7 @@ Size of headers in bytes.
 
   my $chunked = $content->is_chunked;
 
-Chunked transfer encoding.
+Check if content is chunked.
 
 =head2 C<is_done>
 
@@ -553,14 +539,14 @@ Check if parser is done.
 
   my $dynamic = $content->is_dynamic;
 
-Dynamic content.
+Check if content will be dynamic.
 Note that this method is EXPERIMENTAL and might change without warning!
 
 =head2 C<is_multipart>
 
   my $multipart = $content->is_multipart;
 
-Multipart content.
+Check if content is multipart.
 
 =head2 C<is_parsing_body>
 
@@ -572,25 +558,25 @@ Check if body parsing started yet.
 
   my $bytes = $content->leftovers;
 
-Leftovers for next HTTP message.
+Remove leftover data from content parser.
 
 =head2 C<parse>
 
   $content = $content->parse("Content-Length: 12\r\n\r\nHello World!");
 
-Parse content.
+Parse content chunk.
 
 =head2 C<parse_body>
 
   $content = $content->parse_body("Hi!");
 
-Parse body.
+Parse body chunk.
 
 =head2 C<parse_body_once>
 
   $content = $content->parse_body_once("Hi!");
 
-Parse body once.
+Parse body chunk once.
 
 =head2 C<parse_until_body>
 
@@ -598,7 +584,7 @@ Parse body once.
     "Content-Length: 12\r\n\r\nHello World!"
   );
 
-Parse and stop after headers.
+Parse chunk and stop after headers.
 
 =head2 C<progress>
 
@@ -9,14 +9,11 @@ use Mojo::Util 'unquote';
 sub parse {
   my ($self, $string) = @_;
 
+  # Walk tree
   my @cookies;
   my $version = 1;
-
-  # Walk tree
   for my $knot ($self->_tokenize($string)) {
     for my $token (@{$knot}) {
-
-      # Token
       my ($name, $value) = @{$token};
 
       # Value might be quoted
@@ -39,15 +36,13 @@ sub parse {
     }
   }
 
-  return \@cookies;
+  \@cookies;
 }
 
 sub prefix {
   my $self = shift;
-
-  # Prefix
   my $version = $self->version || 1;
-  return "\$Version=$version";
+  "\$Version=$version";
 }
 
 sub to_string {
@@ -60,7 +55,7 @@ sub to_string {
   $cookie .= "=$value" if defined $value && length $value;
   if (my $path = $self->path) { $cookie .= "; \$Path=$path" }
 
-  return $cookie;
+  $cookie;
 }
 
 sub to_string_with_prefix {
@@ -69,7 +64,7 @@ sub to_string_with_prefix {
   # Render with prefix
   my $prefix = $self->prefix;
   my $cookie = $self->to_string;
-  return "$prefix; $cookie";
+  "$prefix; $cookie";
 }
 
 1;
@@ -37,7 +37,7 @@ sub expires {
   $self->{expires} = Mojo::Date->new($self->{expires})
     unless ref $self->{expires};
 
-  return $self->{expires};
+  $self->{expires};
 }
 
 # "Remember the time he ate my goldfish?
@@ -45,8 +45,9 @@ sub expires {
 #  Then why did I have the bowl Bart? Why did I have the bowl?"
 sub parse {
   my ($self, $string) = @_;
-  my @cookies;
 
+  # Walk tree
+  my @cookies;
   for my $knot ($self->_tokenize($string)) {
     for my $i (0 .. $#{$knot}) {
       my ($name, $value) = @{$knot->[$i]};
@@ -74,7 +75,7 @@ sub parse {
     }
   }
 
-  return \@cookies;
+  \@cookies;
 }
 
 sub to_string {
@@ -115,7 +116,7 @@ sub to_string {
   # Comment
   if (my $comment = $self->comment) { $cookie .= "; Comment=$comment" }
 
-  return $cookie;
+  $cookie;
 }
 
 1;
@@ -31,12 +31,12 @@ sub to_string { croak 'Method "to_string" not implemented by subclass' }
 sub _tokenize {
   my ($self, $string) = @_;
 
+  # Nibbling parser
   my (@tree, @token);
   while ($string) {
 
     # Name
     if ($string =~ s/$NAME_RE//o) {
-
       my $name = $1;
       my $value;
 
@@ -67,7 +67,7 @@ sub _tokenize {
   # No separator
   push @tree, [@token] if @token;
 
-  return @tree;
+  @tree;
 }
 
 1;
@@ -16,8 +16,6 @@ sub add {
 
   # Add cookies
   for my $cookie (@cookies) {
-
-    # Unique cookie id
     my $domain = $cookie->domain;
     my $path   = $cookie->path;
     my $name   = $cookie->name;
@@ -28,27 +26,23 @@ sub add {
     # Default to session cookie
     $cookie->max_age(0) unless $cookie->expires || $cookie->max_age;
 
-    # Cookie too big
+    # Check cookie size
     my $value = $cookie->value;
     next if length(defined $value ? $value : '') > $self->max_cookie_size;
 
-    # Initialize
-    $self->{_jar}->{$domain} ||= [];
-
     # Check if we already have a similar cookie
+    $self->{_jar}->{$domain} ||= [];
     my @new;
     for my $old (@{$self->{_jar}->{$domain}}) {
-
-      # Unique cookie id
       push @new, $old unless $old->path eq $path && $old->name eq $name;
     }
 
-    # Add
+    # Yummy!
     push @new, $cookie;
     $self->{_jar}->{$domain} = \@new;
   }
 
-  return $self;
+  $self;
 }
 
 sub empty { shift->{_jar} = {} }
@@ -56,16 +50,12 @@ sub empty { shift->{_jar} = {} }
 sub extract {
   my ($self, $tx) = @_;
 
-  # Fix cookies
+  # Repair cookies and put them in the jar
   my $url     = $tx->req->url;
   my @cookies = @{$tx->res->cookies};
   for my $cookie (@cookies) {
-
-    # Domain
     $cookie->domain($url->host) unless $cookie->domain;
-
-    # Path
-    $cookie->path($url->path) unless $cookie->path;
+    $cookie->path($url->path)   unless $cookie->path;
   }
   $self->add(@cookies);
 }
@@ -73,40 +63,28 @@ sub extract {
 sub find {
   my ($self, $url) = @_;
 
-  # Pattern
+  # Look through the jar
   return unless my $domain = $url->host;
   my $path = $url->path->to_string || '/';
-
-  # Find
   my @found;
   while ($domain =~ /[^\.]+\.[^\.]+|localhost$/) {
-
-    # Nothing
     next unless my $jar = $self->{_jar}->{$domain};
 
-    # Look inside
+    # Grab cookies
     my @new;
     for my $cookie (@$jar) {
 
-      # Session cookie
+      # Check if cookie has expired
       my $session = defined $cookie->max_age && $cookie->max_age > 0 ? 1 : 0;
-      if ($cookie->expires && !$session) {
-
-        # Expired
-        next if time > ($cookie->expires->epoch || 0);
+      if ((my $expires = $cookie->expires) && !$session) {
+        next if time > ($expires->epoch || 0);
       }
-
-      # Not expired
       push @new, $cookie;
 
-      # Port
+      # Taste cookie
       my $port = $url->port || 80;
       next if $cookie->port && $port != $cookie->port;
-
-      # Protocol
       next if $cookie->secure && $url->scheme ne 'https';
-
-      # Path
       my $cpath = $cookie->path;
       push @found,
         Mojo::Cookie::Request->new(
@@ -117,22 +95,21 @@ sub find {
         secure  => $cookie->secure
         ) if $path =~ /^$cpath/;
     }
+
     $self->{_jar}->{$domain} = \@new;
   }
 
-  # Remove leading dot or part
-  continue { $domain =~ s/^(?:\.|[^\.]+)// }
+  # Remove another part
+  continue { $domain =~ s/^(?:\.?[^\.]+)// }
 
-  return @found;
+  @found;
 }
 
 sub inject {
   my ($self, $tx) = @_;
 
-  # Empty jar
+  # Take delicious cookies from the jar
   return unless keys %{$self->{_jar}};
-
-  # Fetch
   my $req = $tx->req;
   my $url = $req->url->clone;
   if (my $host = $req->headers->host) { $url->host($host) }
@@ -153,7 +130,8 @@ Mojo::CookieJar - Cookie Jar For HTTP 1.1 User Agents
 
 =head1 DESCRIPTION
 
-L<Mojo::CookieJar> is a minimalistic cookie jar for HTTP 1.1 user agents.
+L<Mojo::CookieJar> is a minimalistic and relaxed cookie jar for HTTP 1.1 user
+agents.
 
 =head1 ATTRIBUTES
 
@@ -156,7 +156,7 @@ sub new {
   # Parse right away
   $self->parse($xml) if defined $xml;
 
-  return $self;
+  $self;
 }
 
 # DEPRECATED in Smiling Face With Sunglasses!
@@ -186,6 +186,7 @@ sub all_text {
   while (my $e = shift @stack) {
     my $type = $e->[0];
 
+    # Add children of nested tag to stack
     unshift @stack, @$e[4 .. $#$e] and next if $type eq 'tag';
 
     # Text
@@ -201,7 +202,7 @@ sub all_text {
     $text .= $content if $content =~ /\S+/;
   }
 
-  return $text;
+  $text;
 }
 
 sub append { shift->_add(1, @_) }
@@ -210,7 +211,7 @@ sub append_content {
   my ($self, $new) = @_;
   my $tree = $self->tree;
   push @$tree, @{_parent($self->_parse_xml("$new"), $tree->[3])};
-  return $self;
+  $self;
 }
 
 sub at { shift->find(@_)->[0] }
@@ -235,14 +236,14 @@ sub attrs {
     $attrs->{$key} = $values->{$key};
   }
 
-  return $self;
+  $self;
 }
 
 sub charset {
   my $self = shift;
   return $self->[1] if @_ == 0;
   $self->[1] = shift;
-  return $self;
+  $self;
 }
 
 # "Oh boy! Sleep! That's when I'm a Viking!"
@@ -255,7 +256,7 @@ sub children {
   my $start = $tree->[0] eq 'root' ? 1 : 4;
   for my $e (@$tree[$start .. $#$tree]) {
 
-    # Tag
+    # Make sure child is a tag
     next unless $e->[0] eq 'tag';
     next if defined $type && $e->[1] ne $type;
 
@@ -264,20 +265,17 @@ sub children {
       $self->new(charset => $self->charset, tree => $e, xml => $self->xml);
   }
 
-  # Collection
-  return bless \@children, 'Mojo::DOM::_Collection';
+  bless \@children, 'Mojo::DOM::_Collection';
 }
 
 sub content_xml {
   my $self = shift;
 
   # Walk tree
-  my $tree   = $self->tree;
   my $result = '';
+  my $tree   = $self->tree;
   my $start  = $tree->[0] eq 'root' ? 1 : 4;
   for my $e (@$tree[$start .. $#$tree]) {
-
-    # Render
     $result .= $self->_render($e);
   }
 
@@ -285,17 +283,12 @@ sub content_xml {
   my $charset = $self->charset;
   encode $charset, $result if $charset;
 
-  return $result;
+  $result;
 }
 
 sub find {
   my ($self, $css) = @_;
-
-  # Parse CSS selectors
-  my $pattern = $self->_parse_css($css);
-
-  # Filter tree
-  return $self->_match_tree($self->tree, $pattern);
+  $self->_match_tree($self->tree, $self->_parse_css($css));
 }
 
 # DEPRECATED in Smiling Face With Sunglasses!
@@ -309,17 +302,15 @@ EOF
 sub namespace {
   my $self = shift;
 
+  # Prefix
   my $current = $self->tree;
   return if $current->[0] eq 'root';
-
-  # Prefix
   my $prefix = '';
   if ($current->[1] =~ /^(.*?)\:/) { $prefix = $1 }
 
   # Walk tree
   while ($current) {
     return if $current->[0] eq 'root';
-
     my $attrs = $current->[2];
 
     # Namespace for prefix
@@ -355,11 +346,7 @@ sub parent {
 
 sub parse {
   my ($self, $xml) = @_;
-
-  # Detect Perl characters
   $self->charset(undef) if utf8::is_utf8 $xml;
-
-  # Parse
   $self->tree($self->_parse_xml($xml));
 }
 
@@ -370,7 +357,7 @@ sub prepend_content {
   my $tree = $self->tree;
   splice @$tree, $tree->[0] eq 'root' ? 1 : 4, 0,
     @{_parent($self->_parse_xml("$new"), $tree->[3])};
-  return $self;
+  $self;
 }
 
 sub replace {
@@ -393,7 +380,7 @@ sub replace {
   # Replace
   splice @$parent, $i, 1, @{_parent($new, $parent)};
 
-  return $self;
+  $self;
 }
 
 sub replace_content {
@@ -414,7 +401,7 @@ sub replace_content {
   my $start = $tree->[0] eq 'root' ? 1 : 4;
   splice @$tree, $start, $#$tree, @new;
 
-  return $self;
+  $self;
 }
 
 # DEPRECATED in Smiling Face With Sunglasses!
@@ -446,14 +433,10 @@ sub root {
 sub text {
   my $self = shift;
 
-  my $text = '';
-
   # Walk stack
+  my $text = '';
   for my $e (@{$self->tree}) {
-
-    # Meta data
     next unless ref $e eq 'ARRAY';
-
     my $type = $e->[0];
 
     # Text
@@ -469,27 +452,22 @@ sub text {
     $text .= $content if $content =~ /\S+/;
   }
 
-  return $text;
+  $text;
 }
 
 sub to_xml {
-  my $self = shift;
-
-  # Render
-  my $result = $self->_render($self->tree);
-
-  # Encode
+  my $self    = shift;
+  my $result  = $self->_render($self->tree);
   my $charset = $self->charset;
   encode $charset, $result if $charset;
-
-  return $result;
+  $result;
 }
 
 sub tree {
   my $self = shift;
   return $self->[0] if @_ == 0;
   $self->[0] = shift;
-  return $self;
+  $self;
 }
 
 sub type {
@@ -505,14 +483,14 @@ sub type {
   # Set
   $tree->[1] = $type;
 
-  return $self;
+  $self;
 }
 
 sub xml {
   my $self = shift;
   return $self->[2] if @_ == 0;
   $self->[2] = shift;
-  return $self;
+  $self;
 }
 
 sub _add {
@@ -536,7 +514,7 @@ sub _add {
   # Add
   splice @$parent, $i + $offset, 0, @{_parent($new, $parent)};
 
-  return $self;
+  $self;
 }
 
 # "Woah! God is so in your face!
@@ -548,22 +526,18 @@ sub _cdata {
 
 sub _close {
   my ($self, $current, $tags, $stop) = @_;
-
-  # Default to table tags
   $tags ||= \%HTML_TABLE;
-
-  # Default to table tag
   $stop ||= 'table';
 
-  # Check parents
+  # Check if parents need to be closed
   my $parent = $$current;
   while ($parent) {
     last if $parent->[0] eq 'root' || $parent->[1] eq $stop;
 
-    # Match
+    # Close
     $tags->{$parent->[1]} and $self->_end($parent->[1], $current);
 
-    # Next
+    # Try next
     $parent = $parent->[3];
   }
 }
@@ -575,9 +549,9 @@ sub _comment {
 
 sub _css_equation {
   my ($self, $equation) = @_;
-  my $num = [1, 1];
 
   # "even"
+  my $num = [1, 1];
   if ($equation =~ /^even$/i) { $num = [2, 2] }
 
   # "odd"
@@ -592,18 +566,16 @@ sub _css_equation {
     $num->[1] =~ s/\s+//g;
   }
 
-  return $num;
+  $num;
 }
 
 sub _css_regex {
   my ($self, $op, $value) = @_;
-
   return unless $value;
   $value = quotemeta $self->_css_unescape($value);
 
-  my $regex;
-
   # "~=" (word)
+  my $regex;
   if ($op eq '~') { $regex = qr/(?:^|.*\s+)$value(?:\s+.*|$)/ }
 
   # "*=" (contains)
@@ -618,7 +590,7 @@ sub _css_regex {
   # Everything else
   else { $regex = qr/^$value$/ }
 
-  return $regex;
+  $regex;
 }
 
 sub _css_unescape {
@@ -633,7 +605,7 @@ sub _css_unescape {
   # Remove backslash
   $value =~ s/\\//g;
 
-  return $value;
+  $value;
 }
 
 sub _doctype {
@@ -643,7 +615,6 @@ sub _doctype {
 
 sub _end {
   my ($self, $end, $current) = @_;
-
   warn "END $end\n" if DEBUG;
 
   # Not a tag
@@ -655,7 +626,7 @@ sub _end {
   while ($next) {
     last if $next->[0] eq 'root';
 
-    # Found
+    # Right tag
     ++$found and last if $next->[1] eq $end;
 
     # Don't cross block tags that are not optional tags
@@ -664,6 +635,7 @@ sub _end {
         && $HTML_BLOCK{$next->[1]}
         && !$HTML_OPTIONAL{$next->[1]};
 
+    # Parent
     $next = $next->[3];
   }
 
@@ -695,21 +667,18 @@ sub _end {
 sub _match_element {
   my ($self, $candidate, $selectors) = @_;
 
+  # Match
   my @selectors  = reverse @$selectors;
   my $first      = 2;
   my $parentonly = 0;
   my $tree       = $self->tree;
   my ($current, $marker, $snapback, $siblings);
-
-  # Match
   for (my $i = 0; $i <= $#selectors; $i++) {
     my $selector = $selectors[$i];
 
     # Combinator
     $parentonly-- if $parentonly > 0;
     if ($selector->[0] eq 'combinator') {
-
-      # Combinator
       my $c = $selector->[1];
 
       # Parent only ">"
@@ -798,7 +767,7 @@ sub _match_element {
     }
   }
 
-  return 1;
+  1;
 }
 
 sub _match_selector {
@@ -882,10 +851,8 @@ sub _match_selector {
         $args = $c->[2] = $self->_css_equation($args)
           unless ref $args;
 
-        # Parent
-        my $parent = $current->[3];
-
         # Siblings
+        my $parent = $current->[3];
         my $start = $parent->[0] eq 'root' ? 1 : 4;
         my @siblings;
         my $type = $class =~ /of-type$/ ? $current->[1] : undef;
@@ -917,10 +884,8 @@ sub _match_selector {
       elsif ($class =~ /^only-(?:child|(of-type))$/) {
         my $type = $1 ? $current->[1] : undef;
 
-        # Parent
-        my $parent = $current->[3];
-
         # Siblings
+        my $parent = $current->[3];
         my $start = $parent->[0] eq 'root' ? 1 : 4;
         for my $j ($start .. $#$parent) {
           my $sibling = $parent->[$j];
@@ -938,7 +903,7 @@ sub _match_selector {
     return;
   }
 
-  return 1;
+  1;
 }
 
 sub _match_tree {
@@ -966,8 +931,6 @@ sub _match_tree {
 
       # Parts
       for my $part (@$pattern) {
-
-        # Match
         push(@results, $current) and last
           if $self->_match_element($current, $part);
       }
@@ -979,8 +942,7 @@ sub _match_tree {
     $self->new(charset => $self->charset, tree => $_, xml => $self->xml)
   } @results;
 
-  # Collection
-  return bless \@results, 'Mojo::DOM::_Collection';
+  bless \@results, 'Mojo::DOM::_Collection';
 }
 
 sub _parent {
@@ -990,7 +952,7 @@ sub _parent {
     $e->[3] = $parent if $e->[0] eq 'tag';
     push @new, $e;
   }
-  return \@new;
+  \@new;
 }
 
 sub _parse_css {
@@ -1064,22 +1026,19 @@ sub _parse_css {
     push @$part, ['combinator', $combinator] if $combinator;
   }
 
-  return $pattern;
+  $pattern;
 }
 
 sub _parse_xml {
   my ($self, $xml) = @_;
 
-  # State
-  my $tree    = ['root'];
-  my $current = $tree;
-
   # Decode
   my $charset = $self->charset;
   decode $charset, $xml if $charset && !utf8::is_utf8 $xml;
-  return $tree unless $xml;
 
   # Tokenize
+  my $tree    = ['root'];
+  my $current = $tree;
   while ($xml =~ m/\G$XML_TOKEN_RE/gcs) {
     my $text    = $1;
     my $pi      = $2;
@@ -1090,10 +1049,7 @@ sub _parse_xml {
 
     # Text
     if (length $text) {
-
-      # Unescape
       html_unescape $text if (index $text, '&') >= 0;
-
       $self->_text($text, \$current);
     }
 
@@ -1132,13 +1088,11 @@ sub _parse_xml {
         $value = $3 unless defined $value;
         $value = $4 unless defined $value;
 
-        # End
+        # Empty tag
         next if $key eq '/';
 
-        # Unescape
+        # Add unescaped value
         html_unescape $value if $value && (index $value, '&') >= 0;
-
-        # Merge
         $attrs->{$key} = $value;
       }
 
@@ -1159,15 +1113,13 @@ sub _parse_xml {
     }
   }
 
-  return $tree;
+  $tree;
 }
 
+# Try to detect XML from processing instructions
 sub _pi {
   my ($self, $pi, $current) = @_;
-
-  # Try to detect XML
   $self->xml(1) if !defined $self->xml && $pi =~ /xml/i;
-
   push @$$current, ['pi', $pi];
 }
 
@@ -1179,9 +1131,8 @@ sub _raw {
 sub _render {
   my ($self, $tree) = @_;
 
-  my $e = $tree->[0];
-
   # Text (escaped)
+  my $e = $tree->[0];
   if ($e eq 'text') {
     my $escaped = $tree->[1];
     xml_escape $escaped;
@@ -1206,9 +1157,8 @@ sub _render {
   # Offset
   my $start = $e eq 'root' ? 1 : 2;
 
-  my $content = '';
-
   # Start tag
+  my $content = '';
   if ($e eq 'tag') {
 
     # Offset
@@ -1225,10 +1175,8 @@ sub _render {
       # No value
       push @attrs, $key and next unless defined $value;
 
-      # Escape
-      xml_escape $value;
-
       # Key and value
+      xml_escape $value;
       push @attrs, qq/$key="$value"/;
     }
     my $attrs = join ' ', @attrs;
@@ -1242,23 +1190,18 @@ sub _render {
   }
 
   # Walk tree
-  for my $i ($start .. $#$tree) {
-
-    # Render next element
-    $content .= $self->_render($tree->[$i]);
-  }
+  $content .= $self->_render($tree->[$_]) for $start .. $#$tree;
 
   # End tag
   $content .= '</' . $tree->[1] . '>' if $e eq 'tag';
 
-  return $content;
+  $content;
 }
 
 # "It's not important to talk about who got rich off of whom,
 #  or who got exposed to tainted what..."
 sub _start {
   my ($self, $start, $attrs, $current) = @_;
-
   warn "START $start\n" if DEBUG;
 
   # Autoclose optional HTML tags
@@ -1336,10 +1279,10 @@ sub _trim {
   $text =~ s/\s*\n+\s*$//;
   $text =~ s/\s*\n+\s*/\ /g;
 
-  # Add leading whitespace
-  $text = " $text" if $ws;
+  # Add leading whitespace if punctuation allows it
+  $text = " $text" if $ws && $text =~ /^[^\.\!\?\,\;\:]/;
 
-  return $text;
+  $text;
 }
 
 # "Hi, Super Nintendo Chalmers!"
@@ -1364,7 +1307,7 @@ sub _iterate {
 
   # Root
   return unless my $start = $self->[0];
-  return $start->root;
+  $start->root;
 }
 
 1;
@@ -12,8 +12,6 @@ has 'epoch';
 # Days and months
 my @DAYS   = qw/Sun Mon Tue Wed Thu Fri Sat/;
 my @MONTHS = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;
-
-# Reverse months
 my %MONTHS;
 {
   my $i = 0;
@@ -26,7 +24,7 @@ my %MONTHS;
 sub new {
   my $self = shift->SUPER::new();
   $self->parse(@_);
-  return $self;
+  $self;
 }
 
 # "I suggest you leave immediately.
@@ -35,7 +33,6 @@ sub new {
 #  at you?"
 sub parse {
   my ($self, $date) = @_;
-
   return $self unless defined $date;
 
   # epoch - 784111777
@@ -51,9 +48,8 @@ sub parse {
   $date =~ s/GMT\s*$//i;
   $date =~ s/\s+$//;
 
+  # RFC 822/1123 - Sun, 06 Nov 1994 08:49:37 GMT
   my ($day, $month, $year, $hour, $minute, $second);
-
-  # RFC822/1123 - Sun, 06 Nov 1994 08:49:37 GMT
   if ($date =~ /^(\d+)\s+(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)$/) {
     $day    = $1;
     $month  = $MONTHS{$2};
@@ -63,7 +59,7 @@ sub parse {
     $second = $6;
   }
 
-  # RFC850/1036 - Sunday, 06-Nov-94 08:49:37 GMT
+  # RFC 850/1036 - Sunday, 06-Nov-94 08:49:37 GMT
   elsif ($date =~ /^(\d+)-(\w+)-(\d+)\s+(\d+):(\d+):(\d+)$/) {
     $day    = $1;
     $month  = $MONTHS{$2};
@@ -95,17 +91,16 @@ sub parse {
   return $self if $@ || $epoch < 0;
   $self->epoch($epoch);
 
-  return $self;
+  $self;
 }
 
 sub to_string {
   my $self = shift;
 
+  # RFC 822/1123
   my $epoch = $self->epoch;
   $epoch = time unless defined $epoch;
   my ($second, $minute, $hour, $mday, $month, $year, $wday) = gmtime $epoch;
-
-  # Format
   return sprintf(
     "%s, %02d %s %04d %02d:%02d:%02d GMT",
     $DAYS[$wday], $mday, $MONTHS[$month], $year + 1900,
@@ -21,7 +21,7 @@ sub new {
   return $self unless @_;
 
   # Detect
-  return $self->_detect(@_);
+  $self->_detect(@_);
 }
 
 sub throw {
@@ -49,7 +49,7 @@ sub trace {
   }
   $e->frames(\@frames);
 
-  return $e;
+  $e;
 }
 
 sub _detect {
@@ -129,7 +129,7 @@ sub _detect {
   # Context
   $self->_parse_context($line, \@lines) if $line;
 
-  return $self;
+  $self;
 }
 
 # "You killed zombie Flanders!
@@ -140,9 +140,8 @@ sub to_string {
   # Message only
   return $self->message unless $self->verbose;
 
+  # Start with message
   my $string = '';
-
-  # Message
   $string .= $self->message if $self->message;
 
   # Before
@@ -159,7 +158,7 @@ sub to_string {
     $string .= $line->[0] . ': ' . $line->[1] . "\n";
   }
 
-  return $string;
+  $string;
 }
 
 sub _parse_context {
@@ -208,7 +207,7 @@ sub _parse_context {
     }
   }
 
-  return $self;
+  $self;
 }
 
 1;
@@ -99,7 +99,7 @@ sub add {
   push @{$self->{_headers}->{$name}}, (ref $_ || '') eq 'ARRAY' ? $_ : [$_]
     for @_;
 
-  return $self;
+  $self;
 }
 
 sub connection          { scalar shift->header(Connection            => @_) }
@@ -132,7 +132,7 @@ sub from_hash {
     $self->add($header => ref $value eq 'ARRAY' ? @$value : $value);
   }
 
-  return $self;
+  $self;
 }
 
 # "Will you be my mommy? You smell like dead bunnies..."
@@ -152,7 +152,7 @@ sub header {
   return join ', ', map { join ', ', @$_ } @$headers unless wantarray;
 
   # Array
-  return @$headers;
+  @$headers;
 }
 
 sub host { scalar shift->header(Host => @_) }
@@ -177,21 +177,18 @@ sub names {
     push @headers, $NORMALCASE_HEADERS{$name} || $name;
   }
 
-  return \@headers;
+  \@headers;
 }
 
 sub parse {
   my ($self, $chunk) = @_;
 
+  # Parse headers with size limit
+  $self->{_state} = 'headers';
   $self->{_buffer} = '' unless defined $self->{_buffer};
   $self->{_buffer} .= $chunk if defined $chunk;
-
-  # Maximum line size
-  my $max = $self->max_line_size;
-
-  # Parse headers
   my $headers = $self->{_cache} || [];
-  $self->{_state} = 'headers';
+  my $max = $self->max_line_size;
   while (defined(my $line = get_line $self->{_buffer})) {
 
     # Check line size
@@ -233,7 +230,7 @@ sub parse {
     $self->{_limit} = 1;
   }
 
-  return $self;
+  $self;
 }
 
 sub proxy_authenticate  { scalar shift->header('Proxy-Authenticate'  => @_) }
@@ -244,7 +241,7 @@ sub referrer            { scalar shift->header(Referer               => @_) }
 sub remove {
   my ($self, $name) = @_;
   delete $self->{_headers}->{lc $name};
-  return $self;
+  $self;
 }
 
 sub sec_websocket_accept {
@@ -291,7 +288,7 @@ sub to_hash {
     }
   }
 
-  return $hash;
+  $hash;
 }
 
 sub to_string {
@@ -310,7 +307,7 @@ sub to_string {
 
   # Format headers
   my $headers = join "\x0d\x0a", @headers;
-  return length $headers ? $headers : undef;
+  length $headers ? $headers : undef;
 }
 
 sub trailer           { scalar shift->header(Trailer             => @_) }
@@ -17,7 +17,7 @@ sub new {
   $self->log->level('error');
   $self->log->path(undef);
 
-  return $self;
+  $self;
 }
 
 sub handler {
@@ -222,11 +222,8 @@ EOF
 sub _upload {
   my ($self, $tx) = @_;
 
-  # Code
-  my $res = $tx->res;
-  $res->code(200);
-
   # File
+  my $res = $tx->res;
   my $req = $tx->req;
   if (my $file = $req->upload('file')) {
     my $headers = $res->headers;
@@ -255,6 +252,9 @@ sub _upload {
 </html>
 EOF
   }
+
+  # Response
+  $res->code(200);
   $tx->resume;
 }
 
@@ -283,8 +283,14 @@ sub _websocket {
   <head>
     <title>Mojo Diagnostics</title>
     <script language="javascript">
-      if ("WebSocket" in window) {
+      var ws;
+      if ("MozWebSocket" in window) {
+        ws = new MozWebSocket("$url");
+      }
+      else if ("WebSocket" in window) {
         ws = new WebSocket("$url");
+      }
+      if(typeof(ws) !== 'undefined') {
         function wsmessage(event) {
           data = event.data;
           alert(data);
@@ -60,7 +60,7 @@ sub detect {
   # FindBin fallback
   $self->{_parts} = [split /\//, $FindBin::Bin] unless $self->{_parts};
 
-  return $self;
+  $self;
 }
 
 sub lib_dir {
@@ -72,12 +72,13 @@ sub lib_dir {
   return $path if -d $path;
 
   # No lib directory
-  return;
+  undef;
 }
 
 sub list_files {
   my ($self, $dir) = @_;
 
+  # Build portable directory
   my $parts = $self->{_parts} || [];
   my $root = File::Spec->catdir(@$parts);
   $dir = File::Spec->catdir($root, split '/', ($dir || ''));
@@ -108,7 +109,7 @@ sub list_files {
     push @files, @$new;
   }
 
-  return [sort @files];
+  [sort @files];
 }
 
 # "And now to create an unstoppable army of between one million and two
@@ -117,25 +118,25 @@ sub parse {
   my ($self, $path) = @_;
   my @parts = File::Spec->splitdir($path);
   $self->{_parts} = \@parts;
-  return $self;
+  $self;
 }
 
 sub rel_dir {
   my $self = shift;
   my $parts = $self->{_parts} || [];
-  return File::Spec->catdir(@$parts, split '/', shift);
+  File::Spec->catdir(@$parts, split '/', shift);
 }
 
 sub rel_file {
   my $self = shift;
   my $parts = $self->{_parts} || [];
-  return File::Spec->catfile(@$parts, split '/', shift);
+  File::Spec->catfile(@$parts, split '/', shift);
 }
 
 sub to_string {
   my $self = shift;
   my $parts = $self->{_parts} || [];
-  return File::Spec->catdir(@$parts);
+  File::Spec->catdir(@$parts);
 }
 
 1;
@@ -5,58 +5,30 @@ use Carp 'croak';
 use Errno qw/EAGAIN EINTR ECONNRESET EWOULDBLOCK/;
 use File::Spec;
 use IO::File;
-use IO::Poll qw/POLLERR POLLHUP POLLIN POLLOUT/;
 use IO::Socket;
-use List::Util 'first';
-use Mojo::URL;
+use Mojo::IOWatcher;
+use Mojo::Resolver;
 use Scalar::Util 'weaken';
 use Socket qw/IPPROTO_TCP TCP_NODELAY/;
-use Time::HiRes qw/time usleep/;
+use Time::HiRes 'time';
 
 use constant DEBUG      => $ENV{MOJO_IOLOOP_DEBUG} || 0;
 use constant CHUNK_SIZE => $ENV{MOJO_CHUNK_SIZE}   || 131072;
 
-# "AF_INET6" requires Socket6 or Perl 5.12
-use constant IPV6_AF_INET6 => eval { Socket::AF_INET6() }
-  || eval { require Socket6 and Socket6::AF_INET6() };
-
-# "inet_pton" requires Socket6 or Perl 5.12
-BEGIN {
-
-  # Socket
-  if (defined &Socket::inet_pton) { *inet_pton = \&Socket::inet_pton }
-
-  # Socket6
-  elsif (eval { require Socket6 and defined &Socket6::inet_pton }) {
-    *inet_pton = \&Socket6::inet_pton;
-  }
-}
-
-# IPv6 DNS support requires "AF_INET6" and "inet_pton"
-use constant IPV6_DNS => defined IPV6_AF_INET6 && defined &inet_pton;
-
-# IPv6 support requires "AF_INET6", "inet_pton" and IO::Socket::IP
-use constant IPV6 => $ENV{MOJO_NO_IPV6} ? 0 : IPV6_DNS
-  && eval 'use IO::Socket::IP 0.06 (); 1';
+# IPv6 support requires IO::Socket::IP
+use constant IPV6 => $ENV{MOJO_NO_IPV6}
+  ? 0
+  : eval 'use IO::Socket::IP 0.06 (); 1';
 
 # Epoll support requires IO::Epoll
 use constant EPOLL => $ENV{MOJO_POLL}
   ? 0
-  : eval 'use IO::Epoll 0.02 (); 1';
-use constant EPOLL_POLLERR => EPOLL ? IO::Epoll::POLLERR() : 0;
-use constant EPOLL_POLLHUP => EPOLL ? IO::Epoll::POLLHUP() : 0;
-use constant EPOLL_POLLIN  => EPOLL ? IO::Epoll::POLLIN()  : 0;
-use constant EPOLL_POLLOUT => EPOLL ? IO::Epoll::POLLOUT() : 0;
+  : eval 'use Mojo::IOWatcher::Epoll; 1';
 
 # KQueue support requires IO::KQueue
 use constant KQUEUE => $ENV{MOJO_POLL}
   ? 0
-  : eval 'use IO::KQueue 0.34 (); 1';
-use constant KQUEUE_ADD    => KQUEUE ? IO::KQueue::EV_ADD()       : 0;
-use constant KQUEUE_DELETE => KQUEUE ? IO::KQueue::EV_DELETE()    : 0;
-use constant KQUEUE_EOF    => KQUEUE ? IO::KQueue::EV_EOF()       : 0;
-use constant KQUEUE_READ   => KQUEUE ? IO::KQueue::EVFILT_READ()  : 0;
-use constant KQUEUE_WRITE  => KQUEUE ? IO::KQueue::EVFILT_WRITE() : 0;
+  : eval 'use Mojo::IOWatcher::KQueue; 1';
 
 # TLS support requires IO::Socket::SSL
 use constant TLS => $ENV{MOJO_NO_TLS}
@@ -114,52 +86,35 @@ AnqxHi90n/p912ynLg2SjBq+03GaECeGzC/QqKK2gtA=
 -----END RSA PRIVATE KEY-----
 EOF
 
-# DNS server (default to Google Public DNS)
-my $DNS_SERVERS = ['8.8.8.8', '8.8.4.4'];
+has [qw/accept_timeout connect_timeout/] => 3;
+has iowatcher => sub {
 
-# Try to detect DNS server
-if (-r '/etc/resolv.conf') {
-  my $file = IO::File->new;
-  $file->open('< /etc/resolv.conf');
-  my @servers;
-  for my $line (<$file>) {
-
-    # New DNS server
-    if ($line =~ /^nameserver\s+(\S+)$/) {
-      push @servers, $1;
-      warn qq/DETECTED DNS SERVER ($1)\n/ if DEBUG;
-    }
+  # "kqueue"
+  if (KQUEUE) {
+    warn "KQUEUE MAINLOOP\n" if DEBUG;
+    return Mojo::IOWatcher::KQueue->new;
   }
-  unshift @$DNS_SERVERS, @servers;
-}
-
-# User defined DNS server
-unshift @$DNS_SERVERS, $ENV{MOJO_DNS_SERVER} if $ENV{MOJO_DNS_SERVER};
-
-# Always start with first DNS server
-my $CURRENT_DNS_SERVER = 0;
-
-# DNS record types
-my $DNS_TYPES = {
-  '*'   => 0x00ff,
-  A     => 0x0001,
-  AAAA  => 0x001c,
-  CNAME => 0x0005,
-  MX    => 0x000f,
-  NS    => 0x0002,
-  PTR   => 0x000c,
-  TXT   => 0x0010
-};
 
-# "localhost"
-our $LOCALHOST = '127.0.0.1';
+  # "epoll"
+  if (EPOLL) {
+    warn "EPOLL MAINLOOP\n" if DEBUG;
+    return Mojo::IOWatcher::Epoll->new;
+  }
 
-has [qw/accept_timeout connect_timeout dns_timeout/] => 3;
+  # "poll"
+  warn "POLL MAINLOOP\n" if DEBUG;
+  Mojo::IOWatcher->new;
+};
 has max_accepts     => 0;
 has max_connections => 1000;
 has [qw/on_lock on_unlock/] => sub {
   sub {1}
 };
+has resolver => sub {
+  my $self = shift;
+  weaken $self;
+  Mojo::Resolver->new(ioloop => $self);
+};
 has timeout => '0.025';
 
 # Singleton
@@ -167,32 +122,29 @@ our $LOOP;
 
 sub DESTROY {
   my $self = shift;
-
-  # Cleanup connections
-  for my $id (keys %{$self->{_cs}}) { $self->_drop_immediately($id) }
-
-  # Cleanup listen sockets
-  for my $id (keys %{$self->{_listen}}) { $self->_drop_immediately($id) }
-
-  # Cleanup temporary cert file
   if (my $cert = $self->{_cert}) { unlink $cert if -w $cert }
-
-  # Cleanup temporary key file
-  if (my $key = $self->{_key}) { unlink $key if -w $key }
+  if (my $key  = $self->{_key})  { unlink $key  if -w $key }
 }
 
 sub new {
   my $class = shift;
 
-  # Build new loop from singleton if possible
+  # Build new loop from singleton and inherit watcher
   my $loop = $LOOP;
   local $LOOP = undef;
-  my $self = $loop ? $loop->new(@_) : $class->SUPER::new(@_);
+  my $self;
+  if ($loop) {
+    $self = $loop->new(@_);
+    $self->iowatcher($loop->iowatcher->new);
+  }
+
+  # Start from scratch
+  else { $self = $class->SUPER::new(@_) }
 
   # Ignore PIPE signal
   $SIG{PIPE} = 'IGNORE';
 
-  return $self;
+  $self;
 }
 
 sub connect {
@@ -222,9 +174,10 @@ sub connect {
 
   # Lookup
   if (!$args->{handle} && (my $address = $args->{address})) {
-    $self->lookup(
+    weaken $self;
+    $self->resolver->lookup(
       $address => sub {
-        my $self = shift;
+        my $resolver = shift;
         $args->{address} = shift || $args->{address};
         $self->_connect($id, $args);
       }
@@ -234,53 +187,32 @@ sub connect {
   # Connect
   else { $self->_connect($id, $args) }
 
-  return $id;
+  $id;
 }
 
 sub connection_timeout {
   my ($self, $id, $timeout) = @_;
   return unless my $c = $self->{_cs}->{$id};
   $c->{timeout} = $timeout and return $self if $timeout;
-  return $c->{timeout};
-}
-
-sub dns_servers {
-  my $self = shift;
-  $self = $self->singleton unless ref $self;
-
-  # New servers
-  if (@_) {
-    @$DNS_SERVERS       = @_;
-    $CURRENT_DNS_SERVER = 0;
-    return $self;
-  }
-
-  # List all
-  return @$DNS_SERVERS if wantarray;
-
-  # Current server
-  $CURRENT_DNS_SERVER = 0 unless $DNS_SERVERS->[$CURRENT_DNS_SERVER];
-  return $DNS_SERVERS->[$CURRENT_DNS_SERVER];
+  $c->{timeout};
 }
 
 sub drop {
   my ($self, $id) = @_;
   $self = $self->singleton unless ref $self;
 
-  # Drop connection gracefully
+  # Drop connections gracefully
   if (my $c = $self->{_cs}->{$id}) { return $c->{finish} = 1 }
 
-  # Drop
-  return $self->_drop_immediately($id);
+  # Drop everything else right away
+  $self->_drop($id);
 }
 
 sub generate_port {
 
-  # Ports
+  # Try random ports
   my $port = 1 . int(rand 10) . int(rand 10) . int(rand 10) . int(rand 10);
   while ($port++ < 30000) {
-
-    # Try port
     return $port
       if IO::Socket::INET->new(
       Listen    => 5,
@@ -290,13 +222,14 @@ sub generate_port {
       );
   }
 
-  return;
+  undef;
 }
 
 sub idle {
-  my $self = shift;
+  my ($self, $cb) = @_;
   $self = $self->singleton unless ref $self;
-  $self->_add_loop_event(idle => @_);
+  weaken $self;
+  $self->iowatcher->idle(sub { $self->$cb(pop) });
 }
 
 sub is_running {
@@ -312,63 +245,60 @@ sub listen {
   $self = $self->singleton unless ref $self;
   my $args = ref $_[0] ? $_[0] : {@_};
 
+  # No TLS support
   croak "IO::Socket::SSL 1.43 required for TLS support"
     if $args->{tls} && !TLS;
 
-  my %options = (
-    Listen => $args->{backlog} || SOMAXCONN,
-    Proto  => 'tcp',
-    Type   => SOCK_STREAM,
-    %{$args->{args} || {}}
-  );
-  my $file = $args->{file};
-  my $port = $args->{port} || 3000;
-
-  # File descriptor reuse
+  # Look for reusable file descriptor
+  my $file  = $args->{file};
+  my $port  = $args->{port} || 3000;
   my $reuse = defined $file ? $file : $port;
   $ENV{MOJO_REUSE} ||= '';
   my $fd;
   if ($ENV{MOJO_REUSE} =~ /(?:^|\,)$reuse\:(\d+)/) { $fd = $1 }
 
-  # Refresh listen sockets
+  # Stop listening so the new socket has a chance to join
   $self->_not_listening;
 
   # Allow file descriptor inheritance
   local $^F = 1000;
 
   # Listen on UNIX domain socket
-  my $socket;
+  my $handle;
+  my %options = (
+    Listen => $args->{backlog} || SOMAXCONN,
+    Proto  => 'tcp',
+    Type   => SOCK_STREAM,
+    %{$args->{args} || {}}
+  );
   if (defined $file) {
     $options{Local} = $file;
-    $socket =
+    $handle =
       defined $fd
       ? IO::Socket::UNIX->new
       : IO::Socket::UNIX->new(%options)
       or croak "Can't create listen socket: $!";
   }
 
-  # Listen on port
+  # Listen on TCP port
   else {
     $options{LocalAddr} = $args->{address} || '0.0.0.0';
     $options{LocalPort} = $port;
     $options{Proto}     = 'tcp';
     $options{ReuseAddr} = 1;
-
-    # IPv6
     $options{LocalAddr} =~ s/[\[\]]//g;
     my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET';
-
-    $socket = defined $fd ? $class->new : $class->new(%options)
+    $handle = defined $fd ? $class->new : $class->new(%options)
       or croak "Can't create listen socket: $!";
   }
 
-  # File descriptor
+  # Reuse file descriptor
   if (defined $fd) {
-    $socket->fdopen($fd, 'r')
+    $handle->fdopen($fd, 'r')
       or croak "Can't open file descriptor $fd: $!";
   }
   else {
-    $fd = fileno $socket;
+    $fd = fileno $handle;
     $reuse = ",$reuse" if length $ENV{MOJO_REUSE};
     $ENV{MOJO_REUSE} .= "$reuse:$fd";
   }
@@ -383,9 +313,8 @@ sub listen {
   };
   (my $id) = "$c" =~ /0x([\da-f]+)/;
   $self->{_listen}->{$id}      = $c;
-  $self->{_fds}->{$fd}         = $id;
-  $c->{handle}                 = $socket;
-  $self->{_reverse}->{$socket} = $id;
+  $c->{handle}                 = $handle;
+  $self->{_reverse}->{$handle} = $id;
 
   # TLS
   if ($args->{tls}) {
@@ -394,8 +323,6 @@ sub listen {
       SSL_cert_file      => $args->{tls_cert} || $self->_prepare_cert,
       SSL_key_file       => $args->{tls_key} || $self->_prepare_key,
     );
-
-    # Client certificate verification
     %options = (
       SSL_verify_callback => $args->{tls_verify},
       SSL_ca_file         => -T $args->{tls_ca} ? $args->{tls_ca} : undef,
@@ -403,283 +330,84 @@ sub listen {
       SSL_verify_mode     => $args->{tls_ca} ? 0x03 : undef,
       %options
     ) if $args->{tls_ca};
-
     $c->{tls} = {%options, %{$args->{tls_args} || {}}};
   }
 
   # Accept limit
   $self->{_accepts} = $self->max_accepts if $self->max_accepts;
 
-  return $id;
+  $id;
 }
 
 sub local_info {
   my ($self, $id) = @_;
 
-  return {} unless my $c      = $self->{_cs}->{$id};
-  return {} unless my $socket = $c->{handle};
-
   # UNIX domain socket info
-  return {path => $socket->hostpath} if $socket->can('hostpath');
-
-  # Info
-  return {address => $socket->sockhost, port => $socket->sockport};
-}
-
-sub lookup {
-  my ($self, $name, $cb) = @_;
-  $self = $self->singleton unless ref $self;
+  return {} unless my $c      = $self->{_cs}->{$id};
+  return {} unless my $handle = $c->{handle};
+  return {path => $handle->hostpath} if $handle->can('hostpath');
 
-  # "localhost"
-  return $self->timer(0 => sub { shift->$cb($LOCALHOST) })
-    if $name eq 'localhost';
-
-  # IPv4
-  $self->resolve(
-    $name, 'A',
-    sub {
-      my ($self, $records) = @_;
-
-      # Success
-      my $result = first { $_->[0] eq 'A' } @$records;
-      return $self->$cb($result->[1]) if $result;
-
-      # IPv6
-      $self->resolve(
-        $name, 'AAAA',
-        sub {
-          my ($self, $records) = @_;
-
-          # Success
-          my $result = first { $_->[0] eq 'AAAA' } @$records;
-          return $self->$cb($result->[1]) if $result;
-
-          # Pass through
-          $self->$cb();
-        }
-      );
-    }
-  );
+  # TCP socket info
+  {address => $handle->sockhost, port => $handle->sockport};
 }
 
-sub on_close { shift->_add_event(close => @_) }
-sub on_error { shift->_add_event(error => @_) }
-sub on_read  { shift->_add_event(read  => @_) }
+sub on_close { shift->_event(close => @_) }
+sub on_error { shift->_event(error => @_) }
+sub on_read  { shift->_event(read  => @_) }
 
 sub recurring {
-  my $self = shift;
+  my ($self, $after, $cb) = @_;
   $self = $self->singleton unless ref $self;
-  $self->_add_loop_event(timer => pop, after => pop, recurring => time);
+  weaken $self;
+  $self->iowatcher->recurring($after => sub { $self->$cb(pop) });
 }
 
 sub one_tick {
   my ($self, $timeout) = @_;
   $timeout = $self->timeout unless defined $timeout;
 
-  $self->_prepare_listen;
-  $self->_prepare_connections;
-
-  my $loop  = $self->_prepare_loop;
-  my $r     = $self->{_reverse};
-  my $ready = {};
+  # Housekeeping
+  $self->_listening;
+  my $connections = $self->{_cs} ||= {};
+  while (my ($id, $c) = each %$connections) {
 
-  # KQueue
-  if (KQUEUE) {
-
-    # Catch interrupted system call errors
-    my @ret;
-    my $success = eval { @ret = $loop->kevent(1000 * $timeout); 1 };
-    die "KQueue error: $@" if !$success && $@;
-
-    # Events
-    for my $kev (@ret) {
-      my ($fd, $filter, $flags, $fflags) = @$kev;
-      my $id = $self->{_fds}->{$fd};
-      next unless $id;
-      $ready->{$id} += 2 if $filter == KQUEUE_READ || $flags == KQUEUE_EOF;
-      $ready->{$id}++ if $filter == KQUEUE_WRITE;
+    # Connection needs to be finished
+    if ($c->{finish} && !length $c->{buffer} && !$c->{drain}) {
+      $self->_drop($id);
+      next;
     }
-  }
 
-  # Epoll
-  elsif (EPOLL) {
-    $loop->poll($timeout);
-    $ready->{$r->{$_}} += 2
-      for $loop->handles(EPOLL_POLLIN | EPOLL_POLLHUP | EPOLL_POLLERR);
-    $ready->{$r->{$_}}++ for $loop->handles(EPOLL_POLLOUT);
-  }
-
-  # Poll
-  else {
-    $loop->poll($timeout);
-    $ready->{$r->{$_}} += 2 for $loop->handles(POLLIN | POLLHUP | POLLERR);
-    $ready->{$r->{$_}}++ for $loop->handles(POLLOUT);
-  }
-
-  # Handle events
-  for my $id (keys %$ready) {
-
-    # Read
-    if ($ready->{$id} > 1) { $self->_read($id) }
+    # Read only
+    $self->_not_writing($id) if delete $c->{read_only};
 
-    # Write
-    else { $self->_write($id) }
+    # Connection timeout
+    my $time = $c->{active} ||= time;
+    $self->_drop($id) if (time - $time) >= ($c->{timeout} || 15);
   }
 
-  # Handle timers
-  my $timers = $self->_timer;
-
-  # Handle idle events
-  unless (keys %$ready || $timers) {
-    for my $idle (keys %{$self->{_idle}}) {
-      $self->_run_callback('idle', $self->{_idle}->{$idle}->{cb}, $idle);
-    }
+  # Graceful shutdown
+  $self->stop if $self->max_connections == 0 && keys %$connections == 0;
 
-    # Only kqueue blocks when idle
-    usleep 1000000 * $timeout unless KQUEUE;
-  }
+  # Watcher
+  $self->iowatcher->one_tick($timeout);
 }
 
 sub handle {
   my ($self, $id) = @_;
   return unless my $c = $self->{_cs}->{$id};
-  return $c->{handle};
+  $c->{handle};
 }
 
 sub remote_info {
   my ($self, $id) = @_;
 
-  return {} unless my $c      = $self->{_cs}->{$id};
-  return {} unless my $socket = $c->{handle};
-
   # UNIX domain socket info
-  return {path => $socket->peerpath} if $socket->can('peerpath');
-
-  # Info
-  return {address => $socket->peerhost, port => $socket->peerport};
-}
-
-sub resolve {
-  my ($self, $name, $type, $cb) = @_;
-  $self = $self->singleton unless ref $self;
-
-  my $ipv4;
-  $ipv4 = 1 if $name =~ $Mojo::URL::IPV4_RE;
-  my $ipv6;
-  $ipv6 = 1 if IPV6_DNS && $name =~ $Mojo::URL::IPV6_RE;
-
-  my $t      = $DNS_TYPES->{$type};
-  my $server = $self->dns_servers;
-
-  # No lookup required or record type not supported
-  if (!$server || !$t || ($t ne $DNS_TYPES->{PTR} && ($ipv4 || $ipv6))) {
-    $self->timer(0 => sub { $self->$cb([]) });
-    return $self;
-  }
-
-  # Request
-  warn "RESOLVE $type $name ($server)\n" if DEBUG;
-  my $timer;
-  my $tx = int rand 0x10000;
-  my $id = $self->connect(
-    address    => $server,
-    port       => 53,
-    proto      => 'udp',
-    on_connect => sub {
-      my ($self, $id) = @_;
-
-      # Header (one question with recursion)
-      my $req = pack 'nnnnnn', $tx, 0x0100, 1, 0, 0, 0;
-
-      # Reverse
-      my @parts = split /\./, $name;
-      if ($t eq $DNS_TYPES->{PTR}) {
-
-        # IPv4
-        if ($ipv4) { @parts = reverse 'arpa', 'in-addr', @parts }
-
-        # IPv6
-        elsif ($ipv6) {
-          @parts = reverse 'arpa', 'ip6', split //, unpack 'H32',
-            inet_pton(IPV6_AF_INET6, $name);
-        }
-      }
-
-      # Query (Internet)
-      for my $part (@parts) {
-        $req .= pack 'C/a*', $part if defined $part;
-      }
-      $req .= pack 'Cnn', 0, $t, 0x0001;
-
-      $self->write($id => $req);
-    },
-    on_error => sub {
-      my ($self, $id) = @_;
-
-      warn "FAILED $type $name ($server)\n" if DEBUG;
-      $CURRENT_DNS_SERVER++;
-
-      $self->drop($timer) if $timer;
-      $self->$cb([]);
-    },
-    on_read => sub {
-      my ($self, $id, $chunk) = @_;
-
-      # Cleanup
-      $self->drop($id);
-      $self->drop($timer) if $timer;
-
-      my @packet = unpack 'nnnnnna*', $chunk;
-      warn "ANSWERS $packet[3] ($server)\n" if DEBUG;
-
-      # Wrong response
-      return $self->$cb([]) unless $packet[0] eq $tx;
-
-      my $content = $packet[6];
-
-      # Questions
-      for (1 .. $packet[2]) {
-        my $n;
-        do { ($n, $content) = unpack 'C/aa*', $content } while ($n ne '');
-        $content = (unpack 'nna*', $content)[2];
-      }
-
-      # Answers
-      my @answers;
-      for (1 .. $packet[3]) {
-
-        # Parse
-        (my ($t, $ttl, $a), $content) =
-          (unpack 'nnnNn/aa*', $content)[1, 3, 4, 5];
-        my @answer = _parse_answer($t, $a, $chunk, $content);
-
-        # No answer
-        next unless @answer;
-
-        # Answer
-        push @answers, [@answer, $ttl];
-        warn "ANSWER $answer[0] $answer[1]\n" if DEBUG;
-      }
-
-      $self->$cb(\@answers);
-    }
-  );
-
-  # Timer
-  $timer = $self->timer(
-    $self->dns_timeout => sub {
-      my $self = shift;
-
-      warn "RESOLVE TIMEOUT ($server)\n" if DEBUG;
-      $CURRENT_DNS_SERVER++;
-
-      # Abort
-      $self->drop($id);
-      $self->$cb([]);
-    }
-  );
+  return {} unless my $c      = $self->{_cs}->{$id};
+  return {} unless my $handle = $c->{handle};
+  return {path => $handle->peerpath} if $handle->can('peerpath');
 
-  return $self;
+  # TCP socket info
+  {address => $handle->peerhost, port => $handle->peerport};
 }
 
 sub singleton { $LOOP ||= shift->new(@_) }
@@ -695,12 +423,13 @@ sub start {
   # Mainloop
   $self->one_tick while $self->{_running};
 
-  return $self;
+  $self;
 }
 
 sub start_tls {
   my $self = shift;
   my $id   = shift;
+  my $args = ref $_[0] ? $_[0] : {@_};
 
   # No TLS support
   unless (TLS) {
@@ -708,7 +437,13 @@ sub start_tls {
     return;
   }
 
-  my $args = ref $_[0] ? $_[0] : {@_};
+  # Cleanup
+  $self->drop($id) and return unless my $c      = $self->{_cs}->{$id};
+  $self->drop($id) and return unless my $handle = $c->{handle};
+  delete $self->{_reverse}->{$handle};
+  my $watcher = $self->iowatcher->remove($handle);
+
+  # TLS upgrade
   weaken $self;
   my %options = (
     SSL_startHandshake => 0,
@@ -721,30 +456,18 @@ sub start_tls {
     Timeout => $self->connect_timeout,
     %{$args->{tls_args} || {}}
   );
-
-  $self->drop($id) and return unless my $c      = $self->{_cs}->{$id};
-  $self->drop($id) and return unless my $socket = $c->{handle};
-
-  # Cleanup
-  my $fd = fileno $socket;
-  delete $self->{_reverse}->{$socket};
-  my $writing = delete $c->{writing};
-  my $loop    = $self->_prepare_loop;
-  if (KQUEUE) {
-    $loop->EV_SET($fd, KQUEUE_READ,  KQUEUE_DELETE) if defined $writing;
-    $loop->EV_SET($fd, KQUEUE_WRITE, KQUEUE_DELETE) if $writing;
-  }
-  else { $loop->remove($socket) if defined $writing }
-
-  # TLS upgrade
   $self->drop($id) and return
-    unless my $new = IO::Socket::SSL->start_SSL($socket, %options);
+    unless my $new = IO::Socket::SSL->start_SSL($handle, %options);
   $c->{handle} = $new;
   $self->{_reverse}->{$new} = $id;
   $c->{tls_connect} = 1;
-  $self->_writing($id);
+  $watcher->add(
+    $new,
+    on_readable => sub { $self->_read($id) },
+    on_writable => sub { $self->_write($id) }
+  )->writing($new);
 
-  return $id;
+  $id;
 }
 
 sub stop {
@@ -755,29 +478,22 @@ sub stop {
 
 sub test {
   my ($self, $id) = @_;
-
   return unless my $c      = $self->{_cs}->{$id};
-  return unless my $socket = $c->{handle};
-
-  # Test
-  my $test = $self->{_test} ||= IO::Poll->new;
-  $test->mask($socket, POLLIN);
-  $test->poll(0);
-  my $result = $test->handles(POLLIN | POLLERR | POLLHUP);
-  $test->remove($socket);
-
-  return !$result;
+  return unless my $handle = $c->{handle};
+  $self->iowatcher->is_readable($handle);
 }
 
 sub timer {
-  my $self = shift;
+  my ($self, $after, $cb) = @_;
   $self = $self->singleton unless ref $self;
-  $self->_add_loop_event(timer => pop, after => pop, started => time);
+  weaken $self;
+  $self->iowatcher->timer($after => sub { $self->$cb(pop) });
 }
 
 sub write {
   my ($self, $id, $chunk, $cb) = @_;
 
+  # Prepare chunk for writing
   my $c = $self->{_cs}->{$id};
   $c->{buffer} .= $chunk;
 
@@ -796,7 +512,7 @@ sub _accept {
   my ($self, $listen) = @_;
 
   # Accept
-  my $socket = $listen->accept or return;
+  my $handle = $listen->accept or return;
   my $r      = $self->{_reverse};
   my $l      = $self->{_listen}->{$r->{$listen}};
 
@@ -809,20 +525,24 @@ sub _accept {
   weaken $self;
   if (my $tls = $l->{tls}) {
     $tls->{SSL_error_trap} = sub { $self->_error($id, $_[1]) };
-    $socket = IO::Socket::SSL->start_SSL($socket, %$tls);
+    $handle = IO::Socket::SSL->start_SSL($handle, %$tls);
     $c->{tls_accept} = 1;
   }
 
-  $c->{handle} = $socket;
-  $r->{$socket} = $id;
-  my $fd = fileno $socket;
-  $self->{_fds}->{$fd} = $id;
+  # Watch
+  $self->iowatcher->add(
+    $handle,
+    on_readable => sub { $self->_read($id) },
+    on_writable => sub { $self->_write($id) }
+  );
+  $c->{handle} = $handle;
+  $r->{$handle} = $id;
 
   # Non-blocking
-  $socket->blocking(0);
+  $handle->blocking(0);
 
   # Disable Nagle's algorithm
-  setsockopt($socket, IPPROTO_TCP, TCP_NODELAY, 1) unless $l->{file};
+  setsockopt($handle, IPPROTO_TCP, TCP_NODELAY, 1) unless $l->{file};
 
   # Register callbacks
   for my $name (qw/on_close on_error on_read/) {
@@ -830,9 +550,6 @@ sub _accept {
     $self->$name($id => $cb) if $cb;
   }
 
-  # Add socket to mainloop
-  $self->_not_writing($id);
-
   # Accept limit
   $self->max_connections(0)
     if defined $self->{_accepts} && --$self->{_accepts} == 0;
@@ -840,54 +557,31 @@ sub _accept {
   # Accept callback
   warn "ACCEPTED $id\n" if DEBUG;
   my $cb = $c->{on_accept} = $l->{on_accept};
-  $self->_run_event('accept', $cb, $id) if $cb && !$l->{tls};
+  $self->_sandbox('accept', $cb, $id) if $cb && !$l->{tls};
 
   # Stop listening
   $self->_not_listening;
 }
 
-sub _add_event {
-  my ($self, $event, $id, $cb) = @_;
-  return unless my $c = $self->{_cs}->{$id};
-  $c->{$event} = $cb if $cb;
-  return $self;
-}
-
-sub _add_loop_event {
-  my $self  = shift;
-  my $event = shift;
-  my $cb    = shift;
-
-  my $e = {cb => $cb, @_};
-  (my $id) = "$e" =~ /0x([\da-f]+)/;
-  $self->{"_$event"}->{$id} = $e;
-
-  return $id;
-}
-
 sub _connect {
   my ($self, $id, $args) = @_;
 
-  return unless my $c = $self->{_cs}->{$id};
-
-  my %options = (
-    Blocking => 0,
-    PeerAddr => $args->{address},
-    PeerPort => $args->{port} || ($args->{tls} ? 443 : 80),
-    Proto    => $args->{proto},
-    Type     => $args->{proto} eq 'udp' ? SOCK_DGRAM : SOCK_STREAM,
-    %{$args->{args} || {}}
-  );
-
   # Handle
   my $handle;
-  unless ($handle = $args->{handle} || $args->{socket}) {
+  return unless my $c = $self->{_cs}->{$id};
+  unless ($handle = $args->{handle}) {
 
-    # IPv6
+    # New socket
+    my %options = (
+      Blocking => 0,
+      PeerAddr => $args->{address},
+      PeerPort => $args->{port} || ($args->{tls} ? 443 : 80),
+      Proto    => $args->{proto},
+      Type     => $args->{proto} eq 'udp' ? SOCK_DGRAM : SOCK_STREAM,
+      %{$args->{args} || {}}
+    );
     $options{PeerAddr} =~ s/[\[\]]//g if $options{PeerAddr};
     my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET';
-
-    # New socket
     return $self->_error($id, "Couldn't connect.")
       unless $handle = $class->new(%options);
 
@@ -905,35 +599,28 @@ sub _connect {
   # Non-blocking
   $handle->blocking(0);
 
-  return unless defined(my $fd = fileno $handle);
-  $self->{_fds}->{$fd} = $id;
-
-  # Sockets start writing right away
-  $handle->isa('IO::Socket')
-    ? $self->_writing($id)
-    : $self->_not_writing($id);
+  # Start writing right away
+  $self->iowatcher->add(
+    $handle,
+    on_readable => sub { $self->_read($id) },
+    on_writable => sub { $self->_write($id) }
+  )->writing($handle);
 
   # Start TLS
   if ($args->{tls}) { $self->start_tls($id => $args) }
 }
 
-sub _drop_immediately {
+sub _drop {
   my ($self, $id) = @_;
 
-  # Drop loop events
-  for my $event (qw/idle timer/) {
-    if ($self->{"_$event"}->{$id}) {
-      delete $self->{"_$event"}->{$id};
-      return $self;
-    }
-  }
-
-  # Delete connection
-  my $c = delete $self->{_cs}->{$id};
-  delete $self->{_reverse}->{$id};
+  # Cancel watcher events
+  return unless my $watcher = $self->iowatcher;
+  return $self if $watcher->cancel($id);
 
   # Drop listen socket
-  if (!$c && ($c = delete $self->{_listen}->{$id})) {
+  my $c = $self->{_cs}->{$id};
+  if ($c) { return if $c->{drop}++ }
+  elsif ($c = delete $self->{_listen}->{$id}) {
 
     # Not listening
     return $self unless $self->{_listening};
@@ -943,38 +630,23 @@ sub _drop_immediately {
   }
 
   # Delete associated timers
-  if (my $t = $c->{connect_timer} || $c->{accept_timer}) {
-    $self->_drop_immediately($t);
-  }
+  if (my $t = $c->{connect_timer} || $c->{accept_timer}) { $self->_drop($t) }
 
   # Drop handle
   if (my $handle = $c->{handle}) {
     warn "DISCONNECTED $id\n" if DEBUG;
 
     # Handle close
-    if (my $event = $c->{close}) { $self->_run_event('close', $event, $id) }
-
-    # Remove file descriptor
-    return $self unless my $fd = fileno $handle;
-    delete $self->{_fds}->{$fd};
-
-    # Remove handle from kqueue
-    if (my $loop = $self->_prepare_loop) {
-      if (KQUEUE) {
-        my $writing = $c->{writing};
-        $loop->EV_SET($fd, KQUEUE_READ, KQUEUE_DELETE)
-          if defined $writing;
-        $loop->EV_SET($fd, KQUEUE_WRITE, KQUEUE_DELETE) if $writing;
-      }
-
-      # Remove handle from poll or epoll
-      else { $loop->remove($handle) }
-    }
+    if (my $event = $c->{close}) { $self->_sandbox('close', $event, $id) }
 
+    # Delete connection
+    delete $self->{_cs}->{$id};
+    delete $self->{_reverse}->{$id};
+    $watcher->remove($handle);
     close $handle;
   }
 
-  return $self;
+  $self;
 }
 
 sub _error {
@@ -986,141 +658,64 @@ sub _error {
   return unless my $c = $self->{_cs}->{$id};
   my $event = $c->{error};
   warn "Unhandled event error: $error" and return unless $event;
-  $self->_run_event('error', $event, $id, $error);
-  $self->_drop_immediately($id);
+  $self->_sandbox('error', $event, $id, $error);
+  $self->_drop($id);
 }
 
-sub _not_listening {
+sub _event {
+  my ($self, $event, $id, $cb) = @_;
+  return unless my $c = $self->{_cs}->{$id};
+  $c->{$event} = $cb if $cb;
+  $self;
+}
+
+sub _listening {
   my $self = shift;
 
-  # Check loop and unlock
-  return unless my $loop = $self->{_loop};
-  $self->on_unlock->($self);
+  # Already listening or no listen sockets
+  return if $self->{_listening};
+  my $listen = $self->{_listen} ||= {};
+  return unless keys %$listen;
 
-  # Remove listen sockets
-  my $listen = $self->{_listen} || {};
+  # Check if we are allowed to listen and lock
+  my $i = keys %{$self->{_cs}};
+  return unless $i < $self->max_connections;
+  return unless $self->on_lock->($self, !$i);
+
+  # Listen
+  weaken $self;
+  my $watcher = $self->iowatcher;
   for my $lid (keys %$listen) {
-    my $socket = $listen->{$lid}->{handle};
+    $watcher->add($listen->{$lid}->{handle},
+      on_readable => sub { $self->_accept(pop) });
+  }
+  $self->{_listening} = 1;
+}
 
-    # Remove listen socket from kqueue
-    if (KQUEUE) {
-      $loop->EV_SET(fileno $socket, KQUEUE_READ, KQUEUE_DELETE);
-    }
+sub _not_listening {
+  my $self = shift;
 
-    # Remove listen socket from poll or epoll
-    else { $loop->remove($socket) }
-  }
+  # Unlock
+  $self->on_unlock->($self);
 
+  # Stop listening
+  my $listen = $self->{_listen} || {};
+  $self->iowatcher->remove($listen->{$_}->{handle}) for keys %$listen;
   delete $self->{_listening};
 }
 
 sub _not_writing {
   my ($self, $id) = @_;
-
   return unless my $c = $self->{_cs}->{$id};
   return $c->{read_only} = 1 if length $c->{buffer} || $c->{drain};
   return unless my $handle = $c->{handle};
-
-  # Already not writing
-  my $writing = $c->{writing};
-  return if defined $writing && !$writing;
-
-  # KQueue
-  my $loop = $self->_prepare_loop;
-  if (KQUEUE) {
-    my $fd = fileno $handle;
-    $loop->EV_SET($fd, KQUEUE_READ, KQUEUE_ADD) unless defined $writing;
-    $loop->EV_SET($fd, KQUEUE_WRITE, KQUEUE_DELETE) if $writing;
-  }
-
-  # Poll and epoll
-  else {
-    if ($writing) {
-      $loop->remove($handle);
-      $writing = undef;
-    }
-
-    my $mask = EPOLL ? EPOLL_POLLIN : POLLIN;
-    $loop->mask($handle, $mask) unless defined $writing;
-  }
-
-  # Not writing anymore
-  $c->{writing} = 0;
-}
-
-# Answer helper for "resolve"
-sub _parse_answer {
-  my ($t, $a, $packet, $rest) = @_;
-
-  # A
-  if ($t eq $DNS_TYPES->{A}) { return A => join('.', unpack 'C4', $a) }
-
-  # AAAA
-  elsif ($t eq $DNS_TYPES->{AAAA}) {
-    return AAAA => sprintf('%x:%x:%x:%x:%x:%x:%x:%x', unpack('n*', $a));
-  }
-
-  # TXT
-  elsif ($t eq $DNS_TYPES->{TXT}) { return TXT => unpack('(C/a*)*', $a) }
-
-  # Offset
-  my $offset = length($packet) - length($rest) - length($a);
-
-  # CNAME
-  my $type;
-  if ($t eq $DNS_TYPES->{CNAME}) { $type = 'CNAME' }
-
-  # MX
-  elsif ($t eq $DNS_TYPES->{MX}) {
-    $type = 'MX';
-    $offset += 2;
-  }
-
-  # NS
-  elsif ($t eq $DNS_TYPES->{NS}) { $type = 'NS' }
-
-  # PTR
-  elsif ($t eq $DNS_TYPES->{PTR}) { $type = 'PTR' }
-
-  # Domain name
-  return $type => _parse_name($packet, $offset) if $type;
-
-  # Not supported
-  return;
-}
-
-# Domain name helper for "resolve"
-sub _parse_name {
-  my ($packet, $offset) = @_;
-
-  # Elements
-  my @elements;
-  for (1 .. 128) {
-
-    # Element length
-    my $len = ord substr $packet, $offset++, 1;
-
-    # Offset
-    if ($len >= 0xc0) {
-      $offset = (unpack 'n', substr $packet, ++$offset - 2, 2) & 0x3fff;
-    }
-
-    # Element
-    elsif ($len) {
-      push @elements, substr $packet, $offset, $len;
-      $offset += $len;
-    }
-
-    # Zero length element (the end)
-    else { return join '.', @elements }
-  }
-
-  return;
+  $self->iowatcher->not_writing($handle);
 }
 
 sub _prepare_cert {
   my $self = shift;
 
+  # Check if temporary TLS cert file already exists
   my $cert = $self->{_cert};
   return $cert if $cert && -r $cert;
 
@@ -1132,42 +727,13 @@ sub _prepare_cert {
     or croak qq/Can't create temporary TLS cert file "$cert"/;
   print $file CERT;
 
-  return $self->{_cert} = $cert;
-}
-
-sub _prepare_connections {
-  my $self = shift;
-
-  my $cs = $self->{_cs} ||= {};
-
-  # Prepare
-  while (my ($id, $c) = each %$cs) {
-
-    # Connection needs to be finished
-    if ($c->{finish} && !length $c->{buffer} && !$c->{drain}) {
-
-      # Buffer empty
-      $self->_drop_immediately($id);
-      next;
-    }
-
-    # Read only
-    $self->_not_writing($id) if delete $c->{read_only};
-
-    # Last active
-    my $time = $c->{active} ||= time;
-
-    # Connection timeout
-    $self->_drop_immediately($id) if (time - $time) >= ($c->{timeout} || 15);
-  }
-
-  # Graceful shutdown
-  $self->stop if $self->max_connections == 0 && keys %$cs == 0;
+  $self->{_cert} = $cert;
 }
 
 sub _prepare_key {
   my $self = shift;
 
+  # Check if temporary TLS key file already exists
   my $key = $self->{_key};
   return $key if $key && -r $key;
 
@@ -1179,73 +745,12 @@ sub _prepare_key {
     or croak qq/Can't create temporary TLS key file "$key"/;
   print $file KEY;
 
-  return $self->{_key} = $key;
-}
-
-sub _prepare_listen {
-  my $self = shift;
-
-  # Already listening or no listen sockets
-  return if $self->{_listening};
-  my $listen = $self->{_listen} ||= {};
-  return unless keys %$listen;
-
-  # Check if we are allowed to listen
-  my $i = keys %{$self->{_cs}};
-  return unless $i < $self->max_connections;
-  return unless $self->on_lock->($self, !$i);
-
-  # Add listen sockets
-  my $loop = $self->_prepare_loop;
-  for my $lid (keys %$listen) {
-    my $socket = $listen->{$lid}->{handle};
-
-    # KQueue
-    if (KQUEUE) { $loop->EV_SET(fileno $socket, KQUEUE_READ, KQUEUE_ADD) }
-
-    # Epoll
-    elsif (EPOLL) { $loop->mask($socket, EPOLL_POLLIN) }
-
-    # Poll
-    else { $loop->mask($socket, POLLIN) }
-  }
-
-  $self->{_listening} = 1;
-}
-
-sub _prepare_loop {
-  my $self = shift;
-
-  # Already initialized
-  return $self->{_loop} if $self->{_loop};
-
-  # "kqueue"
-  if (KQUEUE) {
-    warn "KQUEUE MAINLOOP\n" if DEBUG;
-    return $self->{_loop} = IO::KQueue->new;
-  }
-
-  # "epoll"
-  elsif (EPOLL) {
-    warn "EPOLL MAINLOOP\n" if DEBUG;
-    $self->{_loop} = IO::Epoll->new;
-  }
-
-  # "poll"
-  else {
-    warn "POLL MAINLOOP\n" if DEBUG;
-    $self->{_loop} = IO::Poll->new;
-  }
-
-  return $self->{_loop};
+  $self->{_key} = $key;
 }
 
 sub _read {
   my ($self, $id) = @_;
 
-  # Listen socket (new connection)
-  if (my $l = $self->{_listen}->{$id}) { $self->_accept($l->{handle}) }
-
   # Check if everything is ready to read
   my $c = $self->{_cs}->{$id};
   return $self->_tls_accept($id)  if $c->{tls_accept};
@@ -1262,83 +767,40 @@ sub _read {
     return if $! == EAGAIN || $! == EINTR || $! == EWOULDBLOCK;
 
     # Connection reset
-    return $self->_drop_immediately($id) if $! == ECONNRESET;
+    return $self->_drop($id) if $! == ECONNRESET;
 
     # Read error
     return $self->_error($id, $!);
   }
 
   # EOF
-  return $self->_drop_immediately($id) if $read == 0;
+  return $self->_drop($id) if $read == 0;
 
   # Handle read
   if (my $event = $c->{read}) {
-    $self->_run_event('read', $event, $id, $buffer);
+    $self->_sandbox('read', $event, $id, $buffer);
   }
 
   # Active
   $c->{active} = time;
 }
 
-# Failed callbacks should not kill everything
-sub _run_callback {
-  my $self  = shift;
-  my $event = shift;
-  my $cb    = shift;
-
-  my $value = eval { $self->$cb(@_) };
-  warn qq/Callback "$event" failed: $@/ if $@;
-
-  return $value;
-}
-
-# Failed events should not kill everything
-sub _run_event {
+sub _sandbox {
   my $self  = shift;
   my $event = shift;
   my $cb    = shift;
   my $id    = shift;
 
+  # Sandbox event
   my $value = eval { $self->$cb($id, @_) };
   if ($@) {
     my $message = qq/Event "$event" failed for connection "$id": $@/;
     $event eq 'error'
-      ? ($self->_drop_immediately($id) and warn $message)
+      ? ($self->_drop($id) and warn $message)
       : $self->_error($id, $message);
   }
 
-  return $value;
-}
-
-sub _timer {
-  my $self = shift;
-
-  # Nothing to do
-  return unless my $ts = $self->{_timer};
-
-  # Check timers
-  my $count = 0;
-  for my $id (keys %$ts) {
-    my $t = $ts->{$id};
-    my $after = $t->{after} || 0;
-    if ($after <= time - ($t->{started} || $t->{recurring} || 0)) {
-      warn "TIMER $id\n" if DEBUG;
-
-      # Normal timer
-      if ($t->{started}) { $self->_drop_immediately($id) }
-
-      # Recurring timer
-      elsif ($after && $t->{recurring}) { $t->{recurring} += $after }
-
-      # Handle timer
-      if (my $cb = $t->{cb}) {
-        $self->_run_callback('timer', $cb);
-        $count++ if $t->{started};
-      }
-    }
-  }
-
-  return $count;
+  $value;
 }
 
 sub _tls_accept {
@@ -1351,11 +813,11 @@ sub _tls_accept {
     # Handle TLS accept
     delete $c->{tls_accept};
     my $cb = $c->{on_accept};
-    $self->_run_event('accept', $cb, $id) if $cb;
+    $self->_sandbox('accept', $cb, $id) if $cb;
     return;
   }
 
-  # Handle error
+  # Switch between reading and writing
   $self->_tls_error($id);
 }
 
@@ -1369,22 +831,18 @@ sub _tls_connect {
     # Handle TLS connect
     delete $c->{tls_connect};
     my $cb = $c->{on_connect};
-    $self->_run_event('connect', $cb, $id) if $cb;
+    $self->_sandbox('connect', $cb, $id) if $cb;
     return;
   }
 
-  # Handle error
+  # Switch between reading and writing
   $self->_tls_error($id);
 }
 
 sub _tls_error {
   my ($self, $id) = @_;
   my $error = $IO::Socket::SSL::SSL_ERROR;
-
-  # Reading
-  if ($error == TLS_READ) { $self->_not_writing($id) }
-
-  # Writing
+  if    ($error == TLS_READ)  { $self->_not_writing($id) }
   elsif ($error == TLS_WRITE) { $self->_writing($id) }
 }
 
@@ -1397,13 +855,11 @@ sub _write {
   return $self->_tls_connect($id) if $c->{tls_connect};
   return unless my $handle = $c->{handle};
 
-  # Connecting
+  # Connected
   if ($c->{connecting}) {
-
-    # Cleanup
     delete $c->{connecting};
     my $timer = delete $c->{connect_timer};
-    $self->_drop_immediately($timer) if $timer;
+    $self->_drop($timer) if $timer;
 
     # Disable Nagle's algorithm
     setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
@@ -1411,11 +867,11 @@ sub _write {
     # Handle connect
     warn "CONNECTED $id\n" if DEBUG;
     my $cb = $c->{on_connect};
-    $self->_run_event('connect', $cb, $id) if $cb && !$c->{tls};
+    $self->_sandbox('connect', $cb, $id) if $cb && !$c->{tls};
   }
 
   # Handle drain
-  $self->_run_event('drain', delete $c->{drain}, $id)
+  $self->_sandbox('drain', delete $c->{drain}, $id)
     if !length $c->{buffer} && $c->{drain};
 
   # Write as much as possible
@@ -1445,32 +901,10 @@ sub _write {
 
 sub _writing {
   my ($self, $id) = @_;
-
-  # Writing again
   my $c = $self->{_cs}->{$id};
   delete $c->{read_only};
-
-  # Already writing or nothing to write to
-  return if my $writing = $c->{writing};
   return unless my $handle = $c->{handle};
-
-  # KQueue
-  my $loop = $self->_prepare_loop;
-  if (KQUEUE) {
-    my $fd = fileno $handle;
-    $loop->EV_SET($fd, KQUEUE_READ,  KQUEUE_ADD) unless defined $writing;
-    $loop->EV_SET($fd, KQUEUE_WRITE, KQUEUE_ADD) unless $writing;
-  }
-
-  # Poll and epoll
-  else {
-    $loop->remove($handle);
-    my $mask = EPOLL ? EPOLL_POLLIN | EPOLL_POLLOUT : POLLIN | POLLOUT;
-    $loop->mask($handle, $mask);
-  }
-
-  # Connection is writing
-  $c->{writing} = 1;
+  $self->iowatcher->writing($handle);
 }
 
 1;
@@ -1559,14 +993,19 @@ dropped, defaults to C<3>.
 Maximum time in seconds a conenction can take to be connected before being
 dropped, defaults to C<3>.
 
-=head2 C<dns_timeout>
+=head2 C<iowatcher>
 
-  my $timeout = $loop->dns_timeout;
-  $loop       = $loop->dns_timeout(5);
+  my $watcher = $loop->iowatcher;
+  $loop       = $loop->iowatcher(Mojo::IOWatcher->new);
 
-Maximum time in seconds a C<DNS> lookup can take, defaults to C<3>.
+Low level event watcher, usually a L<Mojo::IOWatcher>,
+L<Mojo::IOWatcher::KQueue> or L<Mojo::IOLoop->Epoll> object.
+Replacing the event watcher of the singleton loop makes all new loops use the
+same type of event watcher.
 Note that this attribute is EXPERIMENTAL and might change without warning!
 
+  Mojo::IOLoop->singleton->iowatcher(MyWatcher->new);
+
 =head2 C<max_accepts>
 
   my $max = $loop->max_accepts;
@@ -1615,6 +1054,14 @@ Note that exceptions in this callback are not captured.
 A callback to free the accept lock, used to sync multiple server processes.
 Note that exceptions in this callback are not captured.
 
+=head2 C<resolver>
+
+  my $resolver = $loop->resolver;
+  $loop        = $loop->resolver(Mojo::Resolver->new);
+
+DNS stub resolver, usually a L<Mojo::Resolver> object.
+Note that this attribute is EXPERIMENTAL and might change without warning!
+
 =head2 C<timeout>
 
   my $timeout = $loop->timeout;
@@ -1710,17 +1157,6 @@ Path to the TLS key file.
 Maximum amount of time in seconds a connection can be inactive before being
 dropped, defaults to C<15>.
 
-=head2 C<dns_servers>
-
-  my @all     = Mojo::IOLoop->dns_servers;
-  my @all     = $loop->dns_servers;
-  my $current = $loop->dns_servers;
-  $loop       = $loop->dns_servers('8.8.8.8', '8.8.4.4');
-
-IP addresses of C<DNS> servers used for non-blocking lookups, defaults to the
-value of C<MOJO_DNS_SERVER>, auto detection, C<8.8.8.8> or C<8.8.4.4>.
-Note that this method is EXPERIMENTAL and might change without warning!
-
 =head2 C<drop>
 
   $loop = Mojo::IOLoop->drop($id)
@@ -1796,7 +1232,7 @@ A unix domain socket to listen on.
 
 =item C<on_accept>
 
-Callback to invoke for each accepted connection.
+Callback to be invoked for each accepted connection.
 
 =item C<on_close>
 
@@ -1854,19 +1290,6 @@ The local port.
 
 =back
 
-=head2 C<lookup>
-
-  $loop = Mojo::IOLoop->lookup('mojolicio.us' => sub {...});
-  $loop = $loop->lookup('mojolicio.us' => sub {...});
-
-Lookup C<IPv4> or C<IPv6> address for domain.
-Note that this method is EXPERIMENTAL and might change without warning!
-
-  $loop->lookup('mojolicio.us' => sub {
-    my ($loop, $address) = @_;
-    print "Address: $address\n";
-  });
-
 =head2 C<on_close>
 
   $loop = $loop->on_close($id => sub {...});
@@ -1904,8 +1327,9 @@ Run reactor for exactly one tick.
   my $id = Mojo::IOLoop->recurring(0 => sub {...});
   my $id = $loop->recurring(3 => sub {...});
 
-Callback to be invoked on every reactor tick, this for example allows you to
-run multiple reactors next to each other.
+Create a new recurring timer, invoking the callback repeatedly after a given
+amount of seconds.
+This for example allows you to run multiple reactors next to each other.
 
   my $loop2 = Mojo::IOLoop->new(timeout => 0);
   Mojo::IOLoop->recurring(0 => sub { $loop2->one_tick });
@@ -1935,17 +1359,6 @@ The remote port.
 
 =back
 
-=head2 C<resolve>
-
-  $loop = Mojo::IOLoop->resolve('mojolicio.us', 'A', sub {...});
-  $loop = $loop->resolve('mojolicio.us', 'A', sub {...});
-
-Resolve domain into C<A>, C<AAAA>, C<CNAME>, C<MX>, C<NS>, C<PTR> or C<TXT>
-records, C<*> will query for all at once.
-Since this is a "stub resolver" it depends on a recursive name server for DNS
-resolution.
-Note that this method is EXPERIMENTAL and might change without warning!
-
 =head2 C<singleton>
 
   my $loop = Mojo::IOLoop->singleton;
@@ -0,0 +1,37 @@
+package Mojo::IOWatcher::Epoll;
+use Mojo::Base 'Mojo::IOWatcher';
+
+use IO::Epoll 0.02 ':compat';
+use Time::HiRes 'usleep';
+
+# "And America has so many enemies.
+#  Iran, Iraq, China, Mordor, the hoochies that laid low Tiger Woods,
+#  undesirable immigrants - by which I mean everyone that came after me,
+#  including my children..."
+sub _poll { shift->{_poll} ||= IO::Epoll->new }
+
+1;
+__END__
+
+=head1 NAME
+
+Mojo::IOWatcher::Epoll - Epoll Async IO Watcher
+
+=head1 SYNOPSIS
+
+  use Mojo::IOWatcher::Epoll;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOWatcher> is a minimalistic async io watcher with C<epoll> support.
+Note that this module is EXPERIMENTAL and might change without warning!
+
+=head1 METHODS
+
+L<Mojo::IOWatcher::Epoll> inherits all methods from L<Mojo::IOWatcher>.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
+
+=cut
@@ -0,0 +1,117 @@
+package Mojo::IOWatcher::KQueue;
+use Mojo::Base 'Mojo::IOWatcher';
+
+use IO::KQueue 0.34;
+
+# "Wow, Barney. You brought a whole beer keg.
+#  Yeah... where do I fill it up?"
+sub not_writing {
+  my ($self, $handle) = @_;
+
+  my $fd     = fileno $handle;
+  my $h      = $self->{_handles}->{$fd};
+  my $kqueue = $self->_kqueue;
+  $kqueue->EV_SET($fd, EVFILT_READ, EV_ADD)
+    unless defined $h->{writing};
+  $kqueue->EV_SET($fd, EVFILT_WRITE, EV_DELETE) if $h->{writing};
+  $h->{writing} = 0;
+
+  $self;
+}
+
+sub remove {
+  my ($self, $handle) = @_;
+
+  my $fd     = fileno $handle;
+  my $h      = delete $self->{_handles}->{$fd};
+  my $kqueue = $self->_kqueue;
+  $kqueue->EV_SET($fd, EVFILT_READ,  EV_DELETE) if defined $h->{writing};
+  $kqueue->EV_SET($fd, EVFILT_WRITE, EV_DELETE) if $h->{writing};
+
+  $self;
+}
+
+sub watch {
+  my ($self, $timeout) = @_;
+
+  my @ret;
+  eval { @ret = $self->_kqueue->kevent(1000 * $timeout) };
+  my $activity;
+  for my $kev (@ret) {
+    my ($fd, $filter, $flags, $fflags) = @$kev;
+    my $h = $self->{_handles}->{$fd};
+    $self->_sandbox('Read', $h->{on_readable}, $h->{handle})
+      if $filter == EVFILT_READ || $flags == EV_EOF;
+    $self->_sandbox('Write', $h->{on_writable}, $h->{handle})
+      if $filter == EVFILT_WRITE;
+    $activity++;
+  }
+
+  $activity;
+}
+
+sub writing {
+  my ($self, $handle) = @_;
+
+  my $fd = fileno $handle;
+  my $h  = $self->{_handles}->{$fd};
+  $self->_kqueue->EV_SET($fd, EVFILT_READ, EV_ADD)
+    unless defined $h->{writing};
+  $self->_kqueue->EV_SET($fd, EVFILT_WRITE, EV_ADD) unless $h->{writing};
+  $h->{writing} = 1;
+
+  $self;
+}
+
+sub _kqueue { shift->{_kqueue} ||= IO::KQueue->new }
+
+1;
+__END__
+
+=head1 NAME
+
+Mojo::IOWatcher::KQueue - KQueue Async IO Watcher
+
+=head1 SYNOPSIS
+
+  use Mojo::IOWatcher::KQueue;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOWatcher> is a minimalistic async io watcher with C<kqueue> support.
+Note that this module is EXPERIMENTAL and might change without warning!
+
+=head1 METHODS
+
+L<Mojo::IOWatcher::KQueue> inherits all methods from L<Mojo::IOWatcher> and
+implements the following new ones.
+
+=head2 C<not_writing>
+
+  $watcher = $watcher->not_writing($handle);
+
+Only watch handle for readable events.
+
+=head2 C<remove>
+
+  $watcher = $watcher->remove($handle);
+
+Remove handle.
+
+=head2 C<watch>
+
+  my $activity = $watcher->watch('0.25');
+
+Run for exactly one tick and watch only for io events.
+
+=head2 C<writing>
+
+  $watcher = $watcher->writing($handle);
+
+Watch handle for readable and writable events.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
+
+=cut
@@ -0,0 +1,331 @@
+package Mojo::IOWatcher;
+use Mojo::Base -base;
+
+use IO::Poll qw/POLLERR POLLHUP POLLIN POLLOUT/;
+use Time::HiRes 'usleep';
+
+use constant DEBUG => $ENV{MOJO_IOWATCHER_DEBUG} || 0;
+
+# "I don't know.
+#  Can I really betray my country?
+#  I say the Pledge of Allegiance every day.
+#  You pledge allegiance to the flag.
+#  And the flag is made in China."
+sub add {
+  my $self   = shift;
+  my $handle = shift;
+  my $args   = {@_, handle => $handle};
+
+  $self->{_handles}->{fileno $handle} = $args;
+  $args->{on_writable}
+    ? $self->writing($handle)
+    : $self->not_writing($handle);
+
+  $self;
+}
+
+sub cancel {
+  my ($self, $id) = @_;
+  delete $self->{$_}->{$id} and return 1 for qw/_timers _idle/;
+  undef;
+}
+
+sub idle { shift->_event(_idle => shift) }
+
+sub is_readable {
+  my ($self, $handle) = @_;
+
+  # Make sure we watch for readable and writable events
+  my $test = $self->{_test} ||= IO::Poll->new;
+  $test->mask($handle, POLLIN);
+  $test->poll(0);
+  my $result = $test->handles(POLLIN | POLLERR | POLLHUP);
+  $test->remove($handle);
+
+  !$result;
+}
+
+sub not_writing {
+  my ($self, $handle) = @_;
+
+  # Make sure we only watch for readable events
+  my $poll = $self->_poll;
+  $poll->remove($handle)
+    if delete $self->{_handles}->{fileno $handle}->{writing};
+  $poll->mask($handle, $self->POLLIN);
+
+  $self;
+}
+
+sub on_readable {
+  my ($self, $handle, $cb) = @_;
+  $self->{_handles}->{fileno $handle}->{on_readable} = $cb;
+  $self;
+}
+
+sub on_writable {
+  my ($self, $handle, $cb) = @_;
+  $self->{_handles}->{fileno $handle}->{on_writable} = $cb;
+  $self;
+}
+
+# "This was such a pleasant St. Patrick's Day until Irish people showed up."
+sub one_tick {
+  my ($self, $timeout) = @_;
+
+  # IO events
+  my $activity = $self->watch($timeout);
+
+  # Timers
+  my $timers = $self->{_timers} || {};
+  for my $id (keys %$timers) {
+    my $t = $timers->{$id};
+    my $after = $t->{after} || 0;
+    if ($after <= time - ($t->{started} || $t->{recurring} || 0)) {
+      warn "TIMER $id\n" if DEBUG;
+
+      # Normal timer
+      if ($t->{started}) { $self->cancel($id) }
+
+      # Recurring timer
+      elsif ($after && $t->{recurring}) { $t->{recurring} += $after }
+
+      # Handle timer
+      if (my $cb = $t->{cb}) {
+        $self->_sandbox("Timer $id", $cb, $id);
+        $activity++ if $t->{started};
+      }
+    }
+  }
+
+  # Idle
+  unless ($activity) {
+    for my $id (keys %{$self->{_idle} || {}}) {
+      warn "IDLE $id\n" if DEBUG;
+      $self->_sandbox("Idle $id", $self->{_idle}->{$id}->{cb}, $id);
+    }
+  }
+}
+
+sub recurring {
+  my $self = shift;
+  $self->_event(_timers => pop, after => pop, recurring => time);
+}
+
+sub remove {
+  my ($self, $handle) = @_;
+  delete $self->{_handles}->{fileno $handle};
+  $self->_poll->remove($handle);
+  $self;
+}
+
+# "Bart, how did you get a cellphone?
+#  The same way you got me, by accident on a golf course."
+sub timer {
+  my $self = shift;
+  $self->_event(_timers => pop, after => pop, started => time);
+}
+
+sub watch {
+  my ($self, $timeout) = @_;
+
+  # Check for IO events
+  my $poll = $self->_poll;
+  $poll->poll($timeout);
+  my $activity;
+  my $handles = $self->{_handles};
+  for ($poll->handles($self->POLLIN | $self->POLLHUP | $self->POLLERR)) {
+    $self->_sandbox('Read', $handles->{fileno $_}->{on_readable}, $_);
+    $activity++;
+  }
+  for ($poll->handles($self->POLLOUT)) {
+    $self->_sandbox('Write', $handles->{fileno $_}->{on_writable}, $_);
+    $activity++;
+  }
+
+  # Wait for timeout
+  usleep 1000000 * $timeout unless keys %{$self->{_handles}};
+
+  $activity;
+}
+
+sub writing {
+  my ($self, $handle) = @_;
+
+  my $poll = $self->_poll;
+  $poll->remove($handle);
+  $poll->mask($handle, $self->POLLIN | $self->POLLOUT);
+  $self->{_handles}->{fileno $handle}->{writing} = 1;
+
+  $self;
+}
+
+sub _event {
+  my $self = shift;
+  my $pool = shift;
+  my $cb   = shift;
+
+  # Events have an id for easy removal
+  my $e = {cb => $cb, @_};
+  (my $id) = "$e" =~ /0x([\da-f]+)/;
+  $self->{$pool}->{$id} = $e;
+
+  $id;
+}
+
+sub _poll { shift->{_poll} ||= IO::Poll->new }
+
+sub _sandbox {
+  my $self = shift;
+  my $desc = shift;
+  return unless my $cb = shift;
+  warn "$desc failed: $@" unless eval { $self->$cb(@_); 1 };
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojo::IOWatcher - Async IO Watcher
+
+=head1 SYNOPSIS
+
+  use Mojo::IOWatcher;
+
+  # Watch if io handles become readable or writable
+  my $watcher = Mojo::IOWatcher->new;
+  $watcher->add($handle, on_readable => sub {
+    my ($watcher, $handle) = @_;
+    ...
+  });
+
+  # Use timers
+  $watcher->timer(15 => sub {
+    my $watcher = shift;
+    $watcher->remove($handle);
+    print "Timeout!\n";
+  });
+
+  # And loop!
+  $watcher->one_tick('0.25') while 1;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOWatcher> is a minimalistic async io watcher and the foundation of
+L<Mojo::IOLoop>.
+L<Mojo::IOWatcher::KQueue> and L<Mojo::IOWatcher::Epoll> are good examples
+for its extensibility.
+Note that this module is EXPERIMENTAL and might change without warning!
+
+=head1 METHODS
+
+L<Mojo::IOWatcher> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 C<add>
+
+  $watcher = $watcher->add($handle, on_readable => sub {...});
+
+Add handles and watch for io events.
+
+These options are currently available:
+
+=over 2
+
+=item C<on_readable>
+
+Callback to be invoked once the handle becomes readable.
+
+=item C<on_writable>
+
+Callback to be invoked once the handle becomes writable.
+
+=back
+
+=head2 C<cancel>
+
+  my $success = $watcher->cancel($id);
+
+Cancel timer or idle event.
+
+=head2 C<idle>
+
+  my $id = $watcher->idle(sub {...});
+
+Callback to be invoked on every tick if no other events occurred.
+
+=head2 C<is_readable>
+
+  my $readable = $watcher->is_readable($handle);
+
+Quick check if a handle is readable, useful for identifying tainted
+sockets.
+
+=head2 C<not_writing>
+
+  $watcher = $watcher->not_writing($handle);
+
+Only watch handle for readable events.
+
+=head2 C<on_readable>
+
+  $watcher = $watcher->on_readable($handle, sub {...});
+
+Callback to be invoked once the handle becomes readable.
+
+=head2 C<on_writable>
+
+  $watcher = $watcher->on_writable($handle, sub {...});
+
+Callback to be invoked once the handle becomes writable.
+
+=head2 C<one_tick>
+
+  $watcher->one_tick('0.25');
+
+Run for exactly one tick and watch for io, timer and idle events.
+
+=head2 C<recurring>
+
+  my $id = $watcher->recurring(3 => sub {...});
+
+Create a new recurring timer, invoking the callback repeatedly after a given
+amount of seconds.
+
+=head2 C<remove>
+
+  $watcher = $watcher->remove($handle);
+
+Remove handle.
+
+=head2 C<timer>
+
+  my $id = $watcher->timer(3 => sub {...});
+
+Create a new timer, invoking the callback after a given amount of seconds.
+
+=head2 C<watch>
+
+  my $activity = $watcher->watch('0.25');
+
+Run for exactly one tick and watch only for io events.
+
+=head2 C<writing>
+
+  $watcher = $watcher->writing($handle);
+
+Watch handle for readable and writable events.
+
+=head1 DEBUGGING
+
+You can set the C<MOJO_IOWATCHER_DEBUG> environment variable to get some
+advanced diagnostics information printed to C<STDERR>.
+
+  MOJO_IOWATCHER_DEBUG=1
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
+
+=cut
@@ -110,14 +110,14 @@ sub decode {
     $self->error($e);
   }
 
-  return $res;
+  $res;
 }
 
 sub encode {
   my ($self, $ref) = @_;
   my $string = _encode_values($ref);
   Mojo::Util::encode 'UTF-8', $string;
-  return $string;
+  $string;
 }
 
 sub false {$FALSE}
@@ -140,7 +140,7 @@ sub _decode_array {
     _exception('Expected comma or right square bracket while parsing array');
   }
 
-  return \@array;
+  \@array;
 }
 
 sub _decode_object {
@@ -171,13 +171,13 @@ sub _decode_object {
     _exception(q/Expected comma or right curly bracket while parsing object/);
   }
 
-  return \%hash;
+  \%hash;
 }
 
 sub _decode_string {
   my $pos = pos;
 
-  # String
+  # Extract string with escaped characters
   m/\G(((?:[^\x00-\x1F\\"]|\\(?:["\\\/bfnrt]|u[A-Fa-f0-9]{4})){0,32766})*)/gc;
   my $str = $1;
 
@@ -188,13 +188,13 @@ sub _decode_string {
     _exception('Unterminated string');
   }
 
-  # Popular characters
+  # Unescape popular characters
   if (index($str, '\\u') < 0) {
     $str =~ s/\\(["\\\/bfnrt])/$ESCAPE{$1}/gs;
     return $str;
   }
 
-  # Everything else
+  # Unescape everything else
   my $buffer = '';
   while ($str =~ m/\G([^\\]*)\\(?:([^u])|u(.{4}))/gc) {
     $buffer .= $1;
@@ -231,7 +231,7 @@ sub _decode_string {
   # The rest
   $buffer .= substr $str, pos($str), length($str);
 
-  return $buffer;
+  $buffer;
 }
 
 # "Eternity with nerds.
@@ -278,7 +278,7 @@ sub _encode_array {
 
   # Stringify
   my $string = join ',', @array;
-  return "[$string]";
+  "[$string]";
 }
 
 sub _encode_object {
@@ -294,18 +294,18 @@ sub _encode_object {
 
   # Stringify
   my $string = join ',', @values;
-  return "{$string}";
+  "{$string}";
 }
 
 sub _encode_string {
   my $string = shift;
 
-  # Escape
+  # Escape string
   $string
     =~ s/([\x00-\x1F\x7F\x{2028}\x{2029}\\\"\/\b\f\n\r\t])/$REVERSE{$1}/gs;
 
   # Stringify
-  return "\"$string\"";
+  "\"$string\"";
 }
 
 sub _encode_values {
@@ -336,7 +336,7 @@ sub _encode_values {
     if $flags & (B::SVp_IOK | B::SVp_NOK) && !($flags & B::SVp_POK);
 
   # String
-  return _encode_string($value);
+  _encode_string($value);
 }
 
 sub _exception {
@@ -383,7 +383,7 @@ Mojo::JSON - Minimalistic JSON
 
 =head1 DESCRIPTION
 
-L<Mojo::JSON> is a minimalistic and relaxed implementation of RFC4627.
+L<Mojo::JSON> is a minimalistic and relaxed implementation of RFC 4627.
 While it is possibly the fastest pure-Perl JSON parser available, you should
 not use it for validation.
 
@@ -1,20 +1,15 @@
 package Mojo::Loader;
 use Mojo::Base -base;
 
+# "Don't let Krusty's death get you down, boy.
+#  People die all the time, just like that.
+#  Why, you could wake up dead tomorrow! Well, good night."
 use Carp 'carp';
 use File::Basename;
 use File::Spec;
 use Mojo::Command;
 use Mojo::Exception;
 
-use constant DEBUG => $ENV{MOJO_LOADER_DEBUG} || 0;
-
-# Cache stats
-my $STATS = {};
-
-# Debugger sub tracking
-BEGIN { $^P |= 0x10 }
-
 # "Homer no function beer well without."
 sub load {
   my ($self, $module) = @_;
@@ -22,16 +17,8 @@ sub load {
   # Check module name
   return 1 if !$module || $module !~ /^[\w\:\']+$/;
 
-  # Forced reload
-  if ($ENV{MOJO_RELOAD}) {
-    my $key = $module;
-    $key =~ s/\:\:/\//g;
-    $key .= '.pm';
-    _unload($key);
-  }
-
   # Already loaded
-  else { return if $module->can('new') }
+  return if $module->can('new');
 
   # Load
   unless (eval "require $module; 1") {
@@ -44,40 +31,11 @@ sub load {
     return Mojo::Exception->new($@);
   }
 
-  return;
-}
-
-# "Don't let Krusty's death get you down, boy.
-#  People die all the time, just like that.
-#  Why, you could wake up dead tomorrow! Well, good night."
-sub reload {
-
-  # Cleanup script and "main" namespace
-  delete $INC{$0};
-  $STATS->{$0} = 1;
-  _purge(grep { index($_, 'main::') == 0 } keys %DB::sub);
-
-  # Reload
-  while (my ($key, $file) = each %INC) {
-
-    # Modified time
-    next unless $file;
-    my $mtime = (stat $file)[9];
-
-    # Startup time as default
-    $STATS->{$file} = $^T unless defined $STATS->{$file};
-
-    # Modified
-    if ($mtime > $STATS->{$file}) {
-      if (my $e = _reload($key)) { return $e }
-      $STATS->{$file} = $mtime;
-    }
-  }
-
-  # Force script reloading
-  return _reload($0);
+  undef;
 }
 
+# "This is the worst thing you've ever done.
+#  You say that so often that it lost its meaning."
 sub search {
   my ($self, $namespace) = @_;
 
@@ -95,12 +53,9 @@ sub search {
 
     # Check files
     for my $file (@files) {
-      my $full = File::Spec->catfile(File::Spec->splitdir($path), $file);
+      next if -d File::Spec->catfile(File::Spec->splitdir($path), $file);
 
-      # Directory
-      next if -d $full;
-
-      # Found
+      # Module found
       my $name = File::Basename::fileparse($file, qr/\.pm/);
       my $class = "$namespace\::$name";
       push @$modules, $class unless $found{$class};
@@ -109,36 +64,7 @@ sub search {
   }
 
   return unless @$modules;
-  return $modules;
-}
-
-# "This is the worst thing you've ever done.
-#  You say that so often that it lost its meaning."
-sub _purge {
-  for my $sub (@_) {
-    warn "PURGE $sub\n" if DEBUG;
-    carp "Can't unload sub '$sub': $@" unless eval { undef &$sub; 1 };
-    delete $DB::sub{$sub};
-    no strict 'refs';
-    $sub =~ /^(.*::)(.*?)$/ and delete *{$1}->{$2};
-  }
-}
-
-sub _reload {
-  my $key = shift;
-  return if $key eq 'Mojo/Loader.pm';
-  warn "CLEANING $key\n" if DEBUG;
-  _unload($key);
-  warn "RELOADING $key\n" if DEBUG;
-  return Mojo::Exception->new($@)
-    unless eval { package main; require $key; 1 };
-  return;
-}
-
-sub _unload {
-  my $key = shift;
-  return unless my $file = delete $INC{$key};
-  _purge(grep { index($DB::sub{$_}, "$file:") == 0 } keys %DB::sub);
+  $modules;
 }
 
 1;
@@ -156,9 +82,6 @@ Mojo::Loader - Loader
   my $modules = $loader->search('Some::Namespace');
   $loader->load($modules->[0]);
 
-  # Reload
-  Mojo::Loader->reload;
-
 =head1 DESCRIPTION
 
 L<Mojo::Loader> is a class loader and plugin framework.
@@ -172,27 +95,21 @@ following new ones.
 
   my $e = $loader->load('Foo::Bar');
 
-Load a class, note that classes are checked for a C<new> method to see if
-they are already loaded.
-
-=head2 C<reload>
+Load a class and catch exceptions.
+Note that classes are checked for a C<new> method to see if they are already
+loaded.
 
-  my $e = Mojo::Loader->reload;
-
-Reload all Perl files with changes.
+  if (my $e = $loader->load('Foo::Bar')) {
+    die "Exception: $e" if ref $e;
+  }
 
 =head2 C<search>
 
   my $modules = $loader->search('MyApp::Namespace');
 
-Search modules in a namespace.
-
-=head1 DEBUGGING
-
-You can set the C<MOJO_LOADER_DEBUG> environment variable to get some
-advanced diagnostics information printed to C<STDERR>.
+Search for modules in a namespace non-recursively.
 
-  MOJO_LOADER_DEBUG=1
+  $loader->load($_) for @{$loader->search('MyApp::Namespace')};
 
 =head1 SEE ALSO
 
@@ -20,11 +20,12 @@ has handle => sub {
   $file->open(">> $path") or croak qq/Can't open log file "$path": $!/;
   binmode $file, ':utf8';
 
-  return $file;
+  $file;
 };
 has level => 'debug';
 has 'path';
 
+# Supported log level
 my $LEVEL = {debug => 1, info => 2, warn => 3, error => 4, fatal => 5};
 
 # "Yes, I got the most! I win X-Mas!"
@@ -43,7 +44,7 @@ sub is_level {
   return unless $level;
   $level = lc $level;
   my $current = $ENV{MOJO_LOG_LEVEL} || $self->level;
-  return $LEVEL->{$level} >= $LEVEL->{$current};
+  $LEVEL->{$level} >= $LEVEL->{$current};
 }
 
 sub is_warn { shift->is_level('warn') }
@@ -57,10 +58,6 @@ sub log {
   $level = lc $level;
   return $self unless $level && $self->is_level($level);
 
-  my $time = localtime(time);
-  my $msgs = join "\n",
-    map { utf8::decode $_ unless utf8::is_utf8 $_; $_ } @msgs;
-
   # Caller
   my ($pkg, $line) = (caller())[0, 2];
   ($pkg, $line) = (caller(1))[0, 2] if $pkg eq ref $self;
@@ -69,13 +66,16 @@ sub log {
   my $handle = $self->handle;
   flock $handle, LOCK_EX;
 
-  # Log message
+  # Log messages
+  my $time = localtime(time);
+  my $msgs = join "\n",
+    map { utf8::decode $_ unless utf8::is_utf8 $_; $_ } @msgs;
   $handle->syswrite("$time $level $pkg:$line [$$]: $msgs\n");
 
   # Unlock
   flock $handle, LOCK_UN;
 
-  return $self;
+  $self;
 }
 
 sub warn { shift->log('warn', @_) }
@@ -49,7 +49,7 @@ sub cookies {
   }
 
   # No cookies
-  return [];
+  [];
 }
 
 sub fix_headers {
@@ -80,39 +80,35 @@ sub fix_headers {
     }
   }
 
-  return $self;
+  $self;
 }
 
 sub is_secure {
-  my $self = shift;
-
-  # Secure
-  my $url = $self->url;
+  my $self   = shift;
+  my $url    = $self->url;
   my $scheme = $url->scheme || $url->base->scheme || '';
   return 1 if $scheme eq 'https';
-
-  # Not secure
-  return;
+  undef;
 }
 
 sub is_xhr {
   my $self = shift;
   return unless my $with = $self->headers->header('X-Requested-With');
   return 1 if $with =~ /XMLHttpRequest/i;
-  return;
+  undef;
 }
 
 sub param {
   my $self = shift;
   $self->{_params} = $self->params unless $self->{_params};
-  return $self->{_params}->param(@_);
+  $self->{_params}->param(@_);
 }
 
 sub params {
   my $self   = shift;
   my $params = Mojo::Parameters->new;
   $params->merge($self->body_params, $self->query_params);
-  return $params;
+  $params;
 }
 
 sub parse {
@@ -174,7 +170,7 @@ sub parse {
     }
   }
 
-  return $self;
+  $self;
 }
 
 sub proxy {
@@ -192,7 +188,7 @@ sub proxy {
     return $self;
   }
 
-  return $self->{proxy};
+  $self->{proxy};
 }
 
 sub query_params { shift->url->query }
@@ -229,7 +225,7 @@ sub _build_start_line {
   return "$method $path\x0d\x0a" if $version eq '0.9';
 
   # HTTP 1.0 and above
-  return "$method $path HTTP/$version\x0d\x0a";
+  "$method $path HTTP/$version\x0d\x0a";
 }
 
 sub _parse_basic_auth {
@@ -237,7 +233,7 @@ sub _parse_basic_auth {
   return unless $header =~ /Basic (.+)$/;
   my $auth = $1;
   b64_decode $auth;
-  return $auth;
+  $auth;
 }
 
 sub _parse_env {
@@ -247,11 +243,10 @@ sub _parse_env {
   # Make environment accessible
   $self->env($env);
 
+  # Extract headers from environment
   my $headers = $self->headers;
   my $url     = $self->url;
   my $base    = $url->base;
-
-  # Extract headers from environment
   for my $name (keys %$env) {
 
     # Header
@@ -389,13 +384,19 @@ Mojo::Message::Request - HTTP 1.1 Request Container
 
   use Mojo::Message::Request;
 
+  # Parse
+  my $req = Mojo::Message::Request->new;
+  $req->parse("GET /foo HTTP/1.0\x0a\x0d");
+  $req->parse("Content-Length: 12\x0a\x0d\x0a\x0d");
+  $req->parse("Content-Type: text/plain\x0a\x0d\x0a\x0d");
+  $req->parse('Hello World!');
+  print $req->body;
+
+  # Build
   my $req = Mojo::Message::Request->new;
   $req->url->parse('http://127.0.0.1/foo/bar');
   $req->method('GET');
-
-  print "$req";
-
-  $req->parse('GET /foo/bar HTTP/1.1');
+  print $req->to_string;
 
 =head1 DESCRIPTION
 
@@ -421,18 +422,6 @@ Direct access to the environment hash if available.
 
 HTTP request method.
 
-=head2 C<params>
-
-  my $params = $req->params;
-
-All C<GET> and C<POST> parameters, defaults to a L<Mojo::Parameters> object.
-
-=head2 C<query_params>
-
-  my $params = $req->query_params;
-
-All C<GET> parameters, defaults to a L<Mojo::Parameters> object.
-
 =head2 C<url>
 
   my $url = $req->url;
@@ -451,7 +440,7 @@ implements the following new ones.
   $req        = $req->cookies(Mojo::Cookie::Request->new);
   $req        = $req->cookies({name => 'foo', value => 'bar'});
 
-Access request cookies.
+Access request cookies, usually L<Mojo::Cookie::Request> objects.
 
 =head2 C<fix_headers>
 
@@ -475,8 +464,13 @@ Check C<X-Requested-With> header for C<XMLHttpRequest> value.
 
   my $param = $req->param('foo');
 
-Access C<GET> and C<POST> parameters, defaults to a L<Mojo::Parameters>
-object.
+Access C<GET> and C<POST> parameters.
+
+=head2 C<params>
+
+  my $params = $req->params;
+
+All C<GET> and C<POST> parameters, usually a L<Mojo::Parameters> object.
 
 =head2 C<parse>
 
@@ -494,6 +488,12 @@ Parse HTTP request chunks or environment hash.
 
 Proxy URL for message.
 
+=head2 C<query_params>
+
+  my $params = $req->query_params;
+
+All C<GET> parameters, usually a L<Mojo::Parameters> object.
+
 =head1 SEE ALSO
 
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
@@ -101,7 +101,7 @@ sub cookies {
   }
 
   # No cookies
-  return $cookies;
+  $cookies;
 }
 
 sub default_message { $MESSAGES{$_[1] || $_[0]->code || 404} || '' }
@@ -114,29 +114,27 @@ sub fix_headers {
   my $headers = $self->headers;
   $headers->date(Mojo::Date->new->to_string) unless $headers->date;
 
-  return $self;
+  $self;
 }
 
 sub is_status_class {
   my ($self, $class) = @_;
   return unless my $code = $self->code;
   return 1 if $code >= $class && $code < ($class + 100);
-  return;
+  undef;
 }
 
 sub _build_start_line {
   my $self = shift;
 
-  # Version
-  my $version = $self->version;
-
   # HTTP 0.9 has no start line
+  my $version = $self->version;
   return '' if $version eq '0.9';
 
   # HTTP 1.0 and above
   my $code    = $self->code    || 404;
   my $message = $self->message || $self->default_message;
-  return "HTTP/$version $code $message\x0d\x0a";
+  "HTTP/$version $code $message\x0d\x0a";
 }
 
 # "Weaseling out of things is important to learn.
@@ -190,14 +188,20 @@ Mojo::Message::Response - HTTP 1.1 Response Container
 
   use Mojo::Message::Response;
 
+  # Parse
+  my $res = Mojo::Message::Reponse->new;
+  $res->parse("HTTP/1.0 200 OK\x0a\x0d");
+  $res->parse("Content-Length: 12\x0a\x0d\x0a\x0d");
+  $res->parse("Content-Type: text/plain\x0a\x0d\x0a\x0d");
+  $res->parse('Hello World!');
+  print $res->body;
+
+  # Build
   my $res = Mojo::Message::Response->new;
   $res->code(200);
   $res->headers->content_type('text/plain');
   $res->body('Hello World!');
-
-  print "$res";
-
-  $res->parse('HTTP/1.1 200 OK');
+  print $res->to_string;
 
 =head1 DESCRIPTION
 
@@ -234,7 +238,7 @@ implements the following new ones.
   $res        = $res->cookies(Mojo::Cookie::Response->new);
   $req        = $req->cookies({name => 'foo', value => 'bar'});
 
-Access response cookies.
+Access response cookies, usually L<Mojo::Cookie::Response> objects.
 
 =head2 C<default_message>
 
@@ -4,7 +4,8 @@ use Mojo::Base -base;
 use Carp 'croak';
 use Mojo::Asset::Memory;
 use Mojo::Content::Single;
-use Mojo::Loader;
+use Mojo::DOM;
+use Mojo::JSON;
 use Mojo::Parameters;
 use Mojo::Upload;
 use Mojo::Util qw/decode url_unescape/;
@@ -35,7 +36,7 @@ sub at_least_version {
     if $search_major == $current_major && $search_minor <= $current_minor;
 
   # Version is older
-  return;
+  undef;
 }
 
 sub body {
@@ -64,7 +65,7 @@ sub body {
   # Set text content
   elsif (length $new) { $content->asset->add_chunk($new) }
 
-  return $self;
+  $self;
 }
 
 sub body_params {
@@ -73,10 +74,9 @@ sub body_params {
   # Cached
   return $self->{_body_params} if $self->{_body_params};
 
+  # Charset
   my $params = Mojo::Parameters->new;
   my $type = $self->headers->content_type || '';
-
-  # Charset
   $params->charset($self->default_charset);
   $type =~ /charset=\"?(\S+)\"?/ and $params->charset($1);
 
@@ -98,12 +98,13 @@ sub body_params {
       # File
       next if $filename;
 
+      # Form value
       $params->append($name, $value);
     }
   }
 
   # Cache
-  return $self->{_body_params} = $params;
+  $self->{_body_params} = $params;
 }
 
 sub body_size { shift->content->body_size }
@@ -117,7 +118,7 @@ sub build_body {
   my $body = $self->content->build_body(@_);
   $self->{_state} = 'done';
   if (my $cb = $self->on_finish) { $self->$cb }
-  return $body;
+  $body;
 }
 
 sub build_headers {
@@ -127,7 +128,7 @@ sub build_headers {
   return '' if $self->version eq '0.9';
 
   $self->fix_headers;
-  return $self->content->build_headers;
+  $self->content->build_headers;
 }
 
 sub build_start_line {
@@ -149,7 +150,7 @@ sub build_start_line {
     $startline .= $chunk;
   }
 
-  return $startline;
+  $startline;
 }
 
 sub cookie {
@@ -182,8 +183,7 @@ sub cookie {
   my @cookies;
   @cookies = ref $cookies eq 'ARRAY' ? @$cookies : ($cookies) if $cookies;
 
-  # Context
-  return wantarray ? @cookies : $cookies[0];
+  wantarray ? @cookies : $cookies[0];
 }
 
 sub dom {
@@ -192,26 +192,18 @@ sub dom {
   # Multipart
   return if $self->is_multipart;
 
-  # Load DOM class
-  my $class = $self->dom_class;
-  if (my $e = Mojo::Loader->load($class)) {
-    croak ref $e
-      ? qq/Can't load DOM class "$class": $e/
-      : qq/DOM class "$class" doesn't exist./;
-  }
-
   # Charset
   my $charset;
   ($self->headers->content_type || '') =~ /charset=\"?([^\"\s;]+)\"?/
     and $charset = $1;
 
   # Parse
-  my $dom = $class->new(charset => $charset)->parse($self->body);
+  my $dom = $self->dom_class->new(charset => $charset)->parse($self->body);
 
   # Find right away
   return $dom->find(@_) if @_;
 
-  return $dom;
+  $dom;
 }
 
 sub error {
@@ -227,7 +219,7 @@ sub error {
   $self->{_error} = [@_];
   $self->{_state} = 'done';
 
-  return $self;
+  $self;
 }
 
 sub fix_headers {
@@ -244,7 +236,7 @@ sub fix_headers {
     }
   }
 
-  return $self;
+  $self;
 }
 
 sub get_body_chunk {
@@ -261,7 +253,7 @@ sub get_body_chunk {
   $self->{_state} = 'done';
   if (my $cb = $self->on_finish) { $self->$cb }
 
-  return $chunk;
+  $chunk;
 }
 
 sub get_header_chunk {
@@ -273,7 +265,7 @@ sub get_header_chunk {
   # HTTP 0.9 has no headers
   return '' if $self->version eq '0.9';
 
-  return $self->content->get_header_chunk(@_);
+  $self->content->get_header_chunk(@_);
 }
 
 sub get_start_line_chunk {
@@ -284,7 +276,7 @@ sub get_start_line_chunk {
 
   # Get chunk
   my $copy = $self->{_buffer} ||= $self->_build_start_line;
-  return substr $copy, $offset, CHUNK_SIZE;
+  substr $copy, $offset, CHUNK_SIZE;
 }
 
 sub has_leftovers { shift->content->has_leftovers }
@@ -292,7 +284,7 @@ sub has_leftovers { shift->content->has_leftovers }
 sub header_size {
   my $self = shift;
   $self->fix_headers;
-  return $self->content->header_size;
+  $self->content->header_size;
 }
 
 sub headers {
@@ -305,14 +297,14 @@ sub headers {
   }
 
   # Get
-  return $self->content->headers(@_);
+  $self->content->headers(@_);
 }
 
 sub is_chunked { shift->content->is_chunked }
 
 sub is_done {
   return 1 if (shift->{_state} || '') eq 'done';
-  return;
+  undef;
 }
 
 sub is_dynamic { shift->content->is_dynamic }
@@ -321,27 +313,15 @@ sub is_limit_exceeded {
   my $self = shift;
   return unless my $code = ($self->error)[1];
   return unless $code eq '413';
-  return 1;
+  1;
 }
 
 sub is_multipart { shift->content->is_multipart }
 
 sub json {
   my $self = shift;
-
-  # Multipart
   return if $self->is_multipart;
-
-  # Load JSON class
-  my $class = $self->json_class;
-  if (my $e = Mojo::Loader->load($class)) {
-    croak ref $e
-      ? qq/Can't load JSON class "$class": $e/
-      : qq/JSON class "$class" doesn't exist./;
-  }
-
-  # Decode
-  return $class->new->decode($self->body);
+  $self->json_class->new->decode($self->body);
 }
 
 sub leftovers { shift->content->leftovers }
@@ -351,7 +331,7 @@ sub max_line_size { shift->headers->max_line_size(@_) }
 sub param {
   my $self = shift;
   $self->{body_params} ||= $self->body_params;
-  return $self->{body_params}->param(@_);
+  $self->{body_params}->param(@_);
 }
 
 sub parse            { shift->_parse(0, @_) }
@@ -361,12 +341,11 @@ sub start_line_size { length shift->build_start_line }
 
 sub to_string {
   my $self = shift;
-  return $self->build_start_line . $self->build_headers . $self->build_body;
+  $self->build_start_line . $self->build_headers . $self->build_body;
 }
 
 sub upload {
   my ($self, $name) = @_;
-
   return unless $name;
 
   # Map
@@ -395,35 +374,36 @@ sub upload {
   my @uploads;
   @uploads = ref $uploads eq 'ARRAY' ? @$uploads : ($uploads) if $uploads;
 
-  return wantarray ? @uploads : $uploads[0];
+  wantarray ? @uploads : $uploads[0];
 }
 
 sub uploads {
   my $self = shift;
 
+  # Only multipart messages have uplaods
   my @uploads;
   return \@uploads unless $self->is_multipart;
 
+  # Extract formdata
   my $formdata = $self->_parse_formdata;
-
-  # Formdata
   for my $data (@$formdata) {
     my $name     = $data->[0];
     my $filename = $data->[1];
     my $part     = $data->[2];
 
+    # Just a form value
     next unless $filename;
 
+    # Uploaded file
     my $upload = Mojo::Upload->new;
     $upload->name($name);
     $upload->asset($part->asset);
     $upload->filename($filename);
     $upload->headers($part->headers);
-
     push @uploads, $upload;
   }
 
-  return \@uploads;
+  \@uploads;
 }
 
 sub write       { shift->content->write(@_) }
@@ -504,7 +484,7 @@ sub _parse {
   # Finished
   if ((my $cb = $self->on_finish) && $self->is_done) { $self->$cb }
 
-  return $self;
+  $self;
 }
 
 sub _parse_start_line {
@@ -514,9 +494,8 @@ sub _parse_start_line {
 sub _parse_formdata {
   my $self = shift;
 
-  my @formdata;
-
   # Check content
+  my @formdata;
   my $content = $self->content;
   return \@formdata unless $content->is_multipart;
 
@@ -564,8 +543,6 @@ sub _parse_formdata {
 
     # Form value
     unless ($filename) {
-
-      # Slurp
       $value = $part->asset->slurp;
 
       # Decode
@@ -579,7 +556,7 @@ sub _parse_formdata {
     push @formdata, [$name, $filename, $value];
   }
 
-  return \@formdata;
+  \@formdata;
 }
 
 1;
@@ -614,22 +591,23 @@ Content container, defaults to a L<Mojo::Content::Single> object.
   my $charset = $message->default_charset;
   $message    = $message->default_charset('UTF-8');
 
-Default charset used for form data parsing.
+Default charset used for form data parsing, defaults to C<UTF-8>.
 
 =head2 C<dom_class>
 
   my $class = $message->dom_class;
   $message  = $message->dom_class('Mojo::DOM');
 
-Class to be used for DOM manipulation, defaults to L<Mojo::DOM>.
+Class to be used for DOM manipulation with the C<dom> method, defaults to
+L<Mojo::DOM>.
 
 =head2 C<json_class>
 
   my $class = $message->json_class;
   $message  = $message->json_class('Mojo::JSON');
 
-Class to be used for JSON deserialization with C<json>, defaults to
-L<Mojo::JSON>.
+Class to be used for JSON deserialization with the C<json> method, defaults
+to L<Mojo::JSON>.
 
 =head2 C<max_message_size>
 
@@ -645,7 +623,7 @@ Maximum message size in bytes, defaults to C<5242880>.
     my $self = shift;
   });
 
-Callback called after message building or parsing is finished.
+Callback to be invoked after message building or parsing is finished.
 
 =head2 C<on_progress>
 
@@ -655,7 +633,7 @@ Callback called after message building or parsing is finished.
     print '+';
   });
 
-Progress callback.
+Callback to be invoked on progress.
 
 =head1 METHODS
 
@@ -674,13 +652,13 @@ Check if message is at least a specific version.
   $message   = $message->body('Hello!');
   $message   = $message->body(sub {...});
 
-Helper for simplified content access.
+Simple C<content> access.
 
 =head2 C<body_params>
 
   my $params = $message->body_params;
 
-C<POST> parameters.
+C<POST> parameters, usually a L<Mojo::Parameters> object.
 
 =head2 C<body_size>
 
@@ -711,7 +689,8 @@ Render start line.
   my $cookie  = $message->cookie('foo');
   my @cookies = $message->cookie('foo');
 
-Access message cookies.
+Access message cookies, usually L<Mojo::Cookie::Request> or
+L<Mojo::Cookie::Response> objects.
 
 =head2 C<dom>
 
@@ -771,7 +750,7 @@ Size of headers in bytes.
   my $headers = $message->headers;
   $message    = $message->headers(Mojo::Headers->new);
 
-Header container, defaults to a L<Mojo::Headers> object.
+Message headers, defaults to a L<Mojo::Headers> object.
 
 =head2 C<is_chunked>
 
@@ -817,7 +796,7 @@ C<undef> otherwise.
 
   my $bytes = $message->leftovers;
 
-Remove leftover data.
+Remove leftover data from message parser.
 
 =head2 C<max_line_size>
 
@@ -831,7 +810,7 @@ Note that this method is EXPERIMENTAL and might change without warning!
   my $param  = $message->param('foo');
   my @params = $message->param('foo');
 
-Access C<GET> and C<POST> parameters.
+Access C<GET> and C<POST> parameters>.
 
 =head2 C<parse>
 
@@ -862,13 +841,13 @@ Render whole message.
   my $upload  = $message->upload('foo');
   my @uploads = $message->upload('foo');
 
-Access file uploads.
+Access file uploads, usually L<Mojo::Upload> objects.
 
 =head2 C<uploads>
 
   my $uploads = $message->uploads;
 
-All file uploads.
+All file uploads, usually L<Mojo::Upload> objects.
 
 =head2 C<version>
 
@@ -10,7 +10,6 @@ use Mojo::URL;
 
 has charset        => 'UTF-8';
 has pair_separator => '&';
-has params         => sub { [] };
 
 # "Yeah, Moe, that team sure did suck last night. They just plain sucked!
 #  I've seen teams suck before,
@@ -24,9 +23,9 @@ sub new {
   if (@_ > 1) { $self->append(@_) }
 
   # String
-  else { $self->parse(@_) }
+  else { $self->{_string} = $_[0] }
 
-  return $self;
+  $self;
 }
 
 sub append {
@@ -40,21 +39,22 @@ sub append {
   }
   push @{$self->params}, map { defined $_ ? "$_" : '' } @params;
 
-  return $self;
+  $self;
 }
 
 sub clone {
   my $self  = shift;
   my $clone = Mojo::Parameters->new;
   $clone->pair_separator($self->pair_separator);
-  $clone->params([@{$self->params}]);
-  return $clone;
+  if (defined $self->{_string}) { $clone->{_string} = $self->{_string} }
+  else                          { $clone->params([@{$self->params}]) }
+  $clone;
 }
 
 sub merge {
   my $self = shift;
   push @{$self->params}, @{$_->params} for @_;
-  return $self;
+  $self;
 }
 
 sub param {
@@ -75,41 +75,30 @@ sub param {
     push @values, $params->[$i + 1] if $params->[$i] eq $name;
   }
 
-  return wantarray ? @values : $values[0];
+  wantarray ? @values : $values[0];
+}
+
+sub params {
+  my ($self, $params) = @_;
+  if ($params) { $self->{_params} = $params }
+  elsif (defined $self->{_string}) { $self->parse }
+  $self->{_params} ||= [];
 }
 
 sub parse {
-  my $self   = shift;
-  my $string = shift;
+  my ($self, $string) = @_;
+  $string = $self->{_string} unless defined $string;
 
   # Clear
+  delete $self->{_string};
   $self->params([]);
 
-  return $self unless defined $string;
-  my $charset = $self->charset;
-
-  # Detect query string without key/value pairs
-  if ($string !~ /\=/) {
-
-    # Replace "+" with whitespace
-    $string =~ s/\+/\ /g;
-
-    # Escaped string
-    if (index($string, '%') >= 0) {
-      url_unescape $string;
-      my $backup = $string;
-      decode $charset, $string if $charset;
-      $string = $backup unless defined $string;
-    }
-
-    $self->params([$string, undef]);
-    return $self;
-  }
-
   # Detect pair separator for reconstruction
+  return $self unless defined $string && length $string;
   $self->pair_separator(';') if $string =~ /\;/ && $string !~ /\&/;
 
   # W3C suggests to also accept ";" as a separator
+  my $charset = $self->charset;
   for my $pair (split /[\&\;]+/, $string) {
 
     # Parse
@@ -123,15 +112,13 @@ sub parse {
     $name  =~ s/\+/\ /g;
     $value =~ s/\+/\ /g;
 
-    # Escaped name
+    # Unescape
     if (index($name, '%') >= 0) {
       url_unescape $name;
       my $backup = $name;
       decode $charset, $name if $charset;
       $name = $backup unless defined $name;
     }
-
-    # Escaped value
     if (index($value, '%') >= 0) {
       url_unescape $value;
       my $backup = $value;
@@ -142,7 +129,7 @@ sub parse {
     push @{$self->params}, $name, $value;
   }
 
-  return $self;
+  $self;
 }
 
 # "Don't kid yourself, Jimmy. If a cow ever got the chance,
@@ -159,7 +146,7 @@ sub remove {
   }
   $self->params($params);
 
-  return $self;
+  $self;
 }
 
 sub to_hash {
@@ -183,23 +170,32 @@ sub to_hash {
     else { $params{$name} = $value }
   }
 
-  return \%params;
+  \%params;
 }
 
 sub to_string {
   my $self = shift;
 
+  # String
+  my $charset = $self->charset;
+  if (defined(my $string = $self->{_string})) {
+
+    # Escape
+    encode $charset, $string if $charset;
+    url_escape $string, "$Mojo::URL::UNRESERVED\\&\\;\\=\\+\\%";
+
+    return $string;
+  }
+
+  # Build pairs
   my $params = $self->params;
   return '' unless @{$self->params};
-
-  # Format
   my @params;
-  my $charset = $self->charset;
   for (my $i = 0; $i < @$params; $i += 2) {
     my $name  = $params->[$i];
     my $value = $params->[$i + 1];
 
-    # *( pchar / "/" / "?" ) with the exception of ";", "&" and "="
+    # Escape
     encode $charset, $name if $charset;
     url_escape $name, $Mojo::URL::UNRESERVED;
     if ($value) {
@@ -214,8 +210,9 @@ sub to_string {
     push @params, defined $value ? "$name=$value" : "$name";
   }
 
+  # Concatenate pairs
   my $separator = $self->pair_separator;
-  return join $separator, @params;
+  join $separator, @params;
 }
 
 1;
@@ -244,21 +241,14 @@ L<Mojo::Parameters> implements the following attributes.
   my $charset = $params->charset;
   $params     = $params->charset('UTF-8');
 
-Charset used for decoding parameters.
+Charset used for decoding parameters, defaults to C<UTF-8>.
 
 =head2 C<pair_separator>
 
   my $separator = $params->pair_separator;
   $params       = $params->pair_separator(';');
 
-Separator for parameter pairs.
-
-=head2 C<params>
-
-  my $parameters = $params->params;
-  $params        = $params->params(foo => 'b;ar', baz => 23);
-
-The parameters.
+Separator for parameter pairs, defaults to C<&>.
 
 =head1 METHODS
 
@@ -300,6 +290,13 @@ Merge parameters.
 
 Check parameter values.
 
+=head2 C<params>
+
+  my $parameters = $params->params;
+  $params        = $params->params([foo => 'b;ar', baz => 23]);
+
+Parsed parameters.
+
 =head2 C<parse>
 
   $params = $params->parse('foo=b%3Bar&baz=23');
@@ -14,7 +14,7 @@ has parts => sub { [] };
 sub new {
   my $self = shift->SUPER::new();
   $self->parse(@_);
-  return $self;
+  $self;
 }
 
 sub append {
@@ -28,7 +28,8 @@ sub append {
 
     push @{$self->parts}, $value;
   }
-  return $self;
+
+  $self;
 }
 
 sub canonicalize {
@@ -56,7 +57,7 @@ sub canonicalize {
   }
   $self->parts(\@path);
 
-  return $self;
+  $self;
 }
 
 # "Homer, the plant called.
@@ -70,13 +71,13 @@ sub clone {
   $clone->leading_slash($self->leading_slash);
   $clone->trailing_slash($self->trailing_slash);
 
-  return $clone;
+  $clone;
 }
 
 sub parse {
   my ($self, $path) = @_;
 
-  # Meta
+  # Leading and trailing slash
   $path = '' unless defined $path;
   $path =~ /^\// ? $self->leading_slash(1)  : $self->leading_slash(0);
   $path =~ /\/$/ ? $self->trailing_slash(1) : $self->trailing_slash(0);
@@ -96,13 +97,13 @@ sub parse {
   }
   $self->parts(\@parts);
 
-  return $self;
+  $self;
 }
 
 sub to_abs_string {
   my $self = shift;
   return $self->to_string if $self->leading_slash;
-  return '/' . $self->to_string;
+  '/' . $self->to_string;
 }
 
 sub to_string {
@@ -123,7 +124,7 @@ sub to_string {
   $path = "/$path" if $self->leading_slash;
   $path = "$path/" if @path && $self->trailing_slash;
 
-  return $path;
+  $path;
 }
 
 1;
@@ -138,6 +139,8 @@ Mojo::Path - Path
   use Mojo::Path;
 
   my $path = Mojo::Path->new('/foo/bar%3B/baz.html');
+  shift @{$path->parts};
+  print "$path";
 
 =head1 DESCRIPTION
 
@@ -0,0 +1,390 @@
+package Mojo::Resolver;
+use Mojo::Base -base;
+
+use List::Util 'first';
+use Mojo::IOLoop;
+use Mojo::URL;
+
+use constant DEBUG => $ENV{MOJO_RESOLVER_DEBUG} || 0;
+
+# "AF_INET6" requires Socket6 or Perl 5.12
+use constant IPV6_AF_INET6 => eval { Socket::AF_INET6() }
+  || eval { require Socket6 and Socket6::AF_INET6() };
+
+# "inet_pton" requires Socket6 or Perl 5.12
+BEGIN {
+
+  # Socket
+  if (defined &Socket::inet_pton) { *inet_pton = \&Socket::inet_pton }
+
+  # Socket6
+  elsif (eval { require Socket6 and defined &Socket6::inet_pton }) {
+    *inet_pton = \&Socket6::inet_pton;
+  }
+}
+
+# IPv6 DNS support requires "AF_INET6" and "inet_pton"
+use constant IPV6 => defined IPV6_AF_INET6 && defined &inet_pton;
+
+has ioloop => sub { Mojo::IOLoop->new };
+has timeout => 3;
+
+# DNS server (default to Google Public DNS)
+my $SERVERS = ['8.8.8.8', '8.8.4.4'];
+
+# Try to detect DNS server
+if (-r '/etc/resolv.conf') {
+  my $file = IO::File->new;
+  $file->open('< /etc/resolv.conf');
+  my @servers;
+  for my $line (<$file>) {
+
+    # New DNS server
+    if ($line =~ /^nameserver\s+(\S+)$/) {
+      push @servers, $1;
+      warn qq/DETECTED DNS SERVER ($1)\n/ if DEBUG;
+    }
+  }
+  unshift @$SERVERS, @servers;
+}
+
+# User defined DNS server
+unshift @$SERVERS, $ENV{MOJO_DNS_SERVER} if $ENV{MOJO_DNS_SERVER};
+
+# Always start with first DNS server
+my $CURRENT_SERVER = 0;
+
+# DNS record types
+my $DNS_TYPES = {
+  '*'   => 0x00ff,
+  A     => 0x0001,
+  AAAA  => 0x001c,
+  CNAME => 0x0005,
+  MX    => 0x000f,
+  NS    => 0x0002,
+  PTR   => 0x000c,
+  TXT   => 0x0010
+};
+
+# "localhost"
+our $LOCALHOST = '127.0.0.1';
+
+sub lookup {
+  my ($self, $name, $cb) = @_;
+
+  # "localhost"
+  my $loop = $self->ioloop;
+  return $loop->timer(0 => sub { shift->$cb($LOCALHOST) })
+    if $name eq 'localhost';
+
+  # IPv4
+  $self->resolve(
+    $name, 'A',
+    sub {
+      my ($self, $records) = @_;
+
+      # Success
+      my $result = first { $_->[0] eq 'A' } @$records;
+      return $self->$cb($result->[1]) if $result;
+
+      # IPv6
+      $self->resolve(
+        $name, 'AAAA',
+        sub {
+          my ($self, $records) = @_;
+
+          # Success
+          my $result = first { $_->[0] eq 'AAAA' } @$records;
+          return $self->$cb($result->[1]) if $result;
+
+          # Pass through
+          $self->$cb();
+        }
+      );
+    }
+  );
+}
+
+sub resolve {
+  my ($self, $name, $type, $cb) = @_;
+
+  # No lookup required or record type not supported
+  my $ipv4 = $name =~ $Mojo::URL::IPV4_RE ? 1 : 0;
+  my $ipv6   = IPV6 && $name =~ $Mojo::URL::IPV6_RE ? 1 : 0;
+  my $t      = $DNS_TYPES->{$type};
+  my $server = $self->servers;
+  my $loop   = $self->ioloop;
+  if (!$server || !$t || ($t ne $DNS_TYPES->{PTR} && ($ipv4 || $ipv6))) {
+    $loop->timer(0 => sub { $self->$cb([]) });
+    return $self;
+  }
+
+  # Request
+  warn "RESOLVE $type $name ($server)\n" if DEBUG;
+  my $timer;
+  my $tx = int rand 0x10000;
+  my $id = $loop->connect(
+    address    => $server,
+    port       => 53,
+    proto      => 'udp',
+    on_connect => sub {
+      my ($loop, $id) = @_;
+
+      # Header (one question with recursion)
+      my $req = pack 'nnnnnn', $tx, 0x0100, 1, 0, 0, 0;
+
+      # Reverse
+      my @parts = split /\./, $name;
+      if ($t eq $DNS_TYPES->{PTR}) {
+
+        # IPv4
+        if ($ipv4) { @parts = reverse 'arpa', 'in-addr', @parts }
+
+        # IPv6
+        elsif ($ipv6) {
+          @parts = reverse 'arpa', 'ip6', split //, unpack 'H32',
+            inet_pton(IPV6_AF_INET6, $name);
+        }
+      }
+
+      # Query (Internet)
+      for my $part (@parts) {
+        $req .= pack 'C/a*', $part if defined $part;
+      }
+      $req .= pack 'Cnn', 0, $t, 0x0001;
+      $loop->write($id => $req);
+    },
+    on_error => sub {
+      my ($loop, $id) = @_;
+      warn "FAILED $type $name ($server)\n" if DEBUG;
+      $CURRENT_SERVER++;
+      $loop->drop($timer) if $timer;
+      $self->$cb([]);
+    },
+    on_read => sub {
+      my ($loop, $id, $chunk) = @_;
+
+      # Cleanup
+      $loop->drop($id);
+      $loop->drop($timer) if $timer;
+
+      # Check answers
+      my @packet = unpack 'nnnnnna*', $chunk;
+      warn "ANSWERS $packet[3] ($server)\n" if DEBUG;
+      return $self->$cb([]) unless $packet[0] eq $tx;
+
+      # Questions
+      my $content = $packet[6];
+      for (1 .. $packet[2]) {
+        my $n;
+        do { ($n, $content) = unpack 'C/aa*', $content } while ($n ne '');
+        $content = (unpack 'nna*', $content)[2];
+      }
+
+      # Answers
+      my @answers;
+      for (1 .. $packet[3]) {
+
+        # Parse
+        (my ($t, $ttl, $a), $content) =
+          (unpack 'nnnNn/aa*', $content)[1, 3, 4, 5];
+        my @answer = _parse_answer($t, $a, $chunk, $content);
+
+        # No answer
+        next unless @answer;
+
+        # Answer
+        push @answers, [@answer, $ttl];
+        warn "ANSWER $answer[0] $answer[1]\n" if DEBUG;
+      }
+      $self->$cb(\@answers);
+    }
+  );
+
+  # Timer
+  $timer = $loop->timer(
+    $self->timeout => sub {
+      my $loop = shift;
+      warn "RESOLVE TIMEOUT ($server)\n" if DEBUG;
+
+      # Abort
+      $CURRENT_SERVER++;
+      $loop->drop($id);
+      $self->$cb([]);
+    }
+  );
+
+  $self;
+}
+
+sub servers {
+  my $self = shift;
+
+  # New servers
+  if (@_) {
+    @$SERVERS       = @_;
+    $CURRENT_SERVER = 0;
+    return $self;
+  }
+
+  # List all
+  return @$SERVERS if wantarray;
+
+  # Current server
+  $CURRENT_SERVER = 0 unless $SERVERS->[$CURRENT_SERVER];
+  $SERVERS->[$CURRENT_SERVER];
+}
+
+# Answer helper for "resolve"
+sub _parse_answer {
+  my ($t, $a, $packet, $rest) = @_;
+
+  # A
+  if ($t eq $DNS_TYPES->{A}) { return A => join('.', unpack 'C4', $a) }
+
+  # AAAA
+  elsif ($t eq $DNS_TYPES->{AAAA}) {
+    return AAAA => sprintf('%x:%x:%x:%x:%x:%x:%x:%x', unpack('n*', $a));
+  }
+
+  # TXT
+  elsif ($t eq $DNS_TYPES->{TXT}) { return TXT => unpack('(C/a*)*', $a) }
+
+  # Offset
+  my $offset = length($packet) - length($rest) - length($a);
+
+  # CNAME
+  my $type;
+  if ($t eq $DNS_TYPES->{CNAME}) { $type = 'CNAME' }
+
+  # MX
+  elsif ($t eq $DNS_TYPES->{MX}) {
+    $type = 'MX';
+    $offset += 2;
+  }
+
+  # NS
+  elsif ($t eq $DNS_TYPES->{NS}) { $type = 'NS' }
+
+  # PTR
+  elsif ($t eq $DNS_TYPES->{PTR}) { $type = 'PTR' }
+
+  # Domain name
+  return $type => _parse_name($packet, $offset) if $type;
+
+  # Not supported
+  undef;
+}
+
+# Domain name helper for "resolve"
+sub _parse_name {
+  my ($packet, $offset) = @_;
+
+  # Elements
+  my @elements;
+  for (1 .. 128) {
+
+    # Element length
+    my $len = ord substr $packet, $offset++, 1;
+
+    # Offset
+    if ($len >= 0xc0) {
+      $offset = (unpack 'n', substr $packet, ++$offset - 2, 2) & 0x3fff;
+    }
+
+    # Element
+    elsif ($len) {
+      push @elements, substr $packet, $offset, $len;
+      $offset += $len;
+    }
+
+    # Zero length element (the end)
+    else { return join '.', @elements }
+  }
+
+  undef;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojo::Resolver - Async IO DNS Resolver
+
+=head1 SYNOPSIS
+
+  use Mojo::Resolver;
+
+=head1 DESCRIPTION
+
+L<Mojo::Resolver> is a minimalistic async io stub resolver.
+Note that this module is EXPERIMENTAL and might change without warning!
+
+=head1 ATTRIBUTES
+
+L<Mojo::Resolver> implements the following attributes.
+
+=head2 C<ioloop>
+
+  my $ioloop = $resolver->ioloop;
+  $resolver  = $resolver->ioloop(Mojo::IOLoop->new);
+
+Loop object to use for io operations, by default a L<Mojo::IOLoop> object
+will be used.
+
+=head2 C<timeout>
+
+  my $timeout = $resolver->timeout;
+  $resolver   = $resolver->timeout(5);
+
+Maximum time in seconds a C<DNS> lookup can take, defaults to C<3>.
+
+=head1 METHODS
+
+L<Mojo::Resolver> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 C<servers>
+
+  my @all     = $resolver->servers;
+  my $current = $resolver->servers;
+  $resolver   = $resolver->servers('8.8.8.8', '8.8.4.4');
+
+IP addresses of C<DNS> servers used for lookups, defaults to the value of
+C<MOJO_DNS_SERVER>, auto detection, C<8.8.8.8> or C<8.8.4.4>.
+
+=head2 C<lookup>
+
+  $resolver = $resolver->lookup('mojolicio.us' => sub {...});
+
+Lookup C<IPv4> or C<IPv6> address for domain.
+
+  $resolver->lookup('mojolicio.us' => sub {
+    my ($loop, $address) = @_;
+    print "Address: $address\n";
+    Mojo::IOLoop->stop;
+  });
+  Mojo::IOLoop->start;
+
+=head2 C<resolve>
+
+  $resolver = $resolver->resolve('mojolicio.us', 'A', sub {...});
+
+Resolve domain into C<A>, C<AAAA>, C<CNAME>, C<MX>, C<NS>, C<PTR> or C<TXT>
+records, C<*> will query for all at once.
+Since this is a "stub resolver" it depends on a recursive name server for DNS
+resolution.
+
+=head1 DEBUGGING
+
+You can set the C<MOJO_RESOLVER_DEBUG> environment variable to get some
+advanced diagnostics information printed to C<STDERR>.
+
+  MOJO_RESOLVER_DEBUG=1
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
+
+=cut
@@ -11,10 +11,9 @@ has nph => 0;
 sub run {
   my $self = shift;
 
+  # Environment
   my $tx  = $self->on_transaction->($self);
   my $req = $tx->req;
-
-  # Environment
   $req->parse(\%ENV);
 
   # Store connection information
@@ -108,7 +107,7 @@ sub run {
   # Finish transaction
   $tx->on_finish->($tx);
 
-  return $res->code;
+  $res->code;
 }
 
 1;
@@ -23,6 +23,7 @@ has max_clients        => 1000;
 has max_requests       => 25;
 has websocket_timeout  => 300;
 
+# Regex for listen sockets
 my $SOCKET_RE = qr/^
   (http(?:s)?)\:\/\/   # Scheme
   (.+)                 # Host
@@ -38,9 +39,8 @@ my $SOCKET_RE = qr/^
 sub DESTROY {
   my $self = shift;
 
-  return unless my $loop = $self->ioloop;
-
   # Cleanup connections
+  return unless my $loop = $self->ioloop;
   my $cs = $self->{_cs} || {};
   for my $id (keys %$cs) { $loop->drop($id) }
 
@@ -86,10 +86,8 @@ sub setuidgid {
   if (my $group = $self->group) {
     if (my $gid = (getgrnam($group))[2]) {
 
-      # Cleanup
-      undef $!;
-
       # Switch
+      undef $!;
       $) = $gid;
       croak qq/Can't switch to effective group "$group": $!/ if $!;
     }
@@ -99,16 +97,14 @@ sub setuidgid {
   if (my $user = $self->user) {
     if (my $uid = (getpwnam($user))[2]) {
 
-      # Cleanup
-      undef $!;
-
       # Switch
+      undef $!;
       $> = $uid;
       croak qq/Can't switch to effective user "$user": $!/ if $!;
     }
   }
 
-  return $self;
+  $self;
 }
 
 sub _build_tx {
@@ -157,7 +153,7 @@ sub _build_tx {
   # Kept alive if we have more than one request on the connection
   $tx->kept_alive(1) if $c->{requests} > 1;
 
-  return $tx;
+  $tx;
 }
 
 sub _close {
@@ -168,7 +164,7 @@ sub _close {
 sub _drop {
   my ($self, $id) = @_;
 
-  # Finish
+  # Finish gracefully
   my $c = $self->{_cs}->{$id};
   if (my $tx = $c->{websocket} || $c->{transaction}) { $tx->server_close }
 
@@ -238,10 +234,9 @@ sub _listen {
   my ($self, $listen) = @_;
   return unless $listen;
 
+  # UNIX domain socket
   my $options = {};
   my $tls;
-
-  # UNIX domain socket
   if ($listen =~ /^file\:\/\/(.+)$/) { unlink $options->{file} = $1 }
 
   # Internet socket
@@ -294,9 +289,10 @@ sub _listen {
   }
 
   # Friendly message
+  return if $self->silent;
   $self->app->log->info("Server listening ($listen)");
   $listen =~ s/^(https?\:\/\/)\*/${1}127.0.0.1/i;
-  print "Server available at $listen.\n" unless $self->silent;
+  print "Server available at $listen.\n";
 }
 
 sub _read {
@@ -68,7 +68,7 @@ sub accept_connection {
   }
   $self->app->log->debug('Accepted FastCGI connection.') if DEBUG;
 
-  return $c;
+  $c;
 }
 
 sub read_record {
@@ -95,7 +95,7 @@ sub read_record {
       qq/Reading FastCGI record: $type - $id - "$body"./);
   }
 
-  return $self->type_name($type), $id, $body;
+  $self->type_name($type), $id, $body;
 }
 
 sub read_request {
@@ -178,19 +178,19 @@ sub read_request {
     }
   }
 
-  return $tx;
+  $tx;
 }
 
 sub role_name {
   my ($self, $role) = @_;
   return unless $role;
-  return $ROLES[$role - 1];
+  $ROLES[$role - 1];
 }
 
 sub role_number {
   my ($self, $role) = @_;
   return unless $role;
-  return $ROLE_NUMBERS{uc $role};
+  $ROLE_NUMBERS{uc $role};
 }
 
 sub run {
@@ -199,6 +199,7 @@ sub run {
   # Preload application
   $self->app;
 
+  # New incoming request
   while (my $c = $self->accept_connection) {
 
     # Request
@@ -225,28 +226,24 @@ sub run {
 sub type_name {
   my ($self, $type) = @_;
   return unless $type;
-  return $TYPES[$type - 1];
+  $TYPES[$type - 1];
 }
 
 sub type_number {
   my ($self, $type) = @_;
   return unless $type;
-  return $TYPE_NUMBERS{uc $type};
+  $TYPE_NUMBERS{uc $type};
 }
 
 sub write_records {
   my ($self, $c, $type, $id, $body) = @_;
-
-  # Required
   return unless defined $c && defined $type && defined $id;
-
-  # Defaults
   $body ||= '';
-  my $body_len = length $body;
 
   # Write records
-  my $empty = $body ? 0 : 1;
-  my $offset = 0;
+  my $empty    = $body ? 0 : 1;
+  my $offset   = 0;
+  my $body_len = length $body;
   while (($body_len > 0) || $empty) {
 
     # Need to split content
@@ -262,11 +259,11 @@ sub write_records {
         qq/Writing FastCGI record: $type - $id - "$chunk"./);
     }
 
+    # Write whole record
     my $record = pack $template, 1, $self->type_number($type), $id,
       $payload_len,
       $pad_len,
       substr($body, $offset, $payload_len);
-
     my $woffset = 0;
     while ($woffset < length $record) {
       my $written = $c->syswrite($record, undef, $woffset);
@@ -283,25 +280,23 @@ sub write_records {
 
       $woffset += $written;
     }
-
     $body_len -= $payload_len;
     $offset += $payload_len;
 
+    # Done
     last if $empty;
   }
 
-  return 1;
+  1;
 }
 
 sub write_response {
   my ($self, $tx) = @_;
   $self->app->log->debug('Writing FastCGI response.') if DEBUG;
 
-  my $c   = $tx->connection;
-  my $res = $tx->res;
-
   # Status
-  my $code    = $res->code    || 404;
+  my $res     = $tx->res;
+  my $code    = $res->code || 404;
   my $message = $res->message || $res->default_message;
   $res->headers->status("$code $message") unless $res->headers->status;
 
@@ -309,6 +304,7 @@ sub write_response {
   $res->fix_headers;
 
   # Headers
+  my $c      = $tx->connection;
   my $offset = 0;
   while (1) {
     my $chunk = $res->get_header_chunk($offset);
@@ -369,7 +365,7 @@ sub _nv_length {
     $len = unpack 'N', $len;
   }
 
-  return $len;
+  $len;
 }
 
 sub _read_chunk {
@@ -387,7 +383,7 @@ sub _read_chunk {
     $chunk .= $buffer;
   }
 
-  return $chunk;
+  $chunk;
 }
 
 1;
@@ -14,9 +14,7 @@ use POSIX qw/setsid WNOHANG/;
 use Scalar::Util 'weaken';
 
 # Preload
-use Mojo::DOM;
 use Mojo::UserAgent;
-use Mojolicious::Controller;
 
 use constant DEBUG => $ENV{HYPNOTOAD_DEBUG} || 0;
 
@@ -58,24 +56,18 @@ sub run {
   # Config
   $ENV{HYPNOTOAD_CONFIG} ||= abs_path $config;
 
-  # Production
+  # This is a production server
   $ENV{MOJO_MODE} ||= 'production';
 
   # Executable
   $ENV{HYPNOTOAD_EXE} ||= $0;
   $0 = $ENV{HYPNOTOAD_APP};
 
-  # Cleanup
-  delete $ENV{MOJO_COMMANDS_DONE};
-  delete $ENV{MOJO_RELOAD};
-
   # Clean start
   exec $ENV{HYPNOTOAD_EXE} unless $ENV{HYPNOTOAD_REV}++;
 
-  # Daemon
-  my $daemon = $self->{_daemon} = Mojo::Server::Daemon->new;
-
   # Preload application
+  my $daemon = $self->{_daemon} = Mojo::Server::Daemon->new;
   warn "APPLICATION $ENV{HYPNOTOAD_APP}\n" if DEBUG;
   $daemon->load_app($ENV{HYPNOTOAD_APP});
 
@@ -108,10 +100,8 @@ sub run {
     open STDERR, '>&STDOUT';
   }
 
-  # Config
-  my $c = $self->{_config};
-
   # Manager signals
+  my $c = $self->{_config};
   $SIG{INT} = $SIG{TERM} = sub { $self->{_done} = 1 };
   $SIG{CHLD} = sub {
     while ((my $pid = waitpid -1, WNOHANG) > 0) { $self->_reap($pid) }
@@ -134,11 +124,9 @@ sub run {
 sub _config {
   my $self = shift;
 
-  # File
+  # Load config file
   my $file = $ENV{HYPNOTOAD_CONFIG};
   warn "CONFIG $file\n" if DEBUG;
-
-  # Config
   my $c = {};
   if (-r $file) {
     unless ($c = do $file) {
@@ -150,84 +138,46 @@ sub _config {
   }
   $self->{_config} = $c;
 
-  # Graceful timeout
-  $c->{graceful_timeout} ||= 30;
-
-  # Heartbeat interval
+  # Hypnotoad settings
+  $c->{graceful_timeout}   ||= 30;
   $c->{heartbeat_interval} ||= 5;
-
-  # Heartbeat timeout
-  $c->{heartbeat_timeout} ||= 2;
-
-  # Lock file
+  $c->{heartbeat_timeout}  ||= 2;
   $c->{lock_file}
     ||= File::Spec->catfile($ENV{MOJO_TMPDIR} || File::Spec->tmpdir,
     "hypnotoad.$$.lock");
-
-  # PID file
   $c->{pid_file}
     ||= File::Spec->catfile(dirname($ENV{HYPNOTOAD_APP}), 'hypnotoad.pid');
-
-  # Reverse proxy support
-  $ENV{MOJO_REVERSE_PROXY} = 1 if $c->{proxy};
-
-  # Upgrade timeout
   $c->{upgrade_timeout} ||= 30;
+  $c->{workers}         ||= 4;
 
-  # Workers
-  $c->{workers} ||= 4;
-
-  # Daemon
+  # Daemon settings
+  $ENV{MOJO_REVERSE_PROXY} = 1 if $c->{proxy};
   my $daemon = $self->{_daemon};
-
-  # Backlog
   $daemon->backlog($c->{backlog}) if defined $c->{backlog};
-
-  # Clients
   $daemon->max_clients($c->{clients} || 1000);
-
-  # Group
   $daemon->group($c->{group}) if $c->{group};
-
-  # Keep alive requests
-  $daemon->max_requests($c->{keep_alive_requests} || 25);
-
-  # Keep alive timeout
+  $daemon->max_requests($c->{keep_alive_requests}      || 25);
   $daemon->keep_alive_timeout($c->{keep_alive_timeout} || 5);
-
-  # Listen
-  my $listen = $c->{listen} || ['http://*:8080'];
-  $listen = [$listen] unless ref $listen;
-  $daemon->listen($listen);
-
-  # User
   $daemon->user($c->{user}) if $c->{user};
-
-  # WebSocket timeout
   $daemon->websocket_timeout($c->{websocket_timeout} || 300);
-
-  # Accept limit
   $daemon->ioloop->max_accepts($c->{accepts} || 1000);
+  my $listen = $c->{listen} || ['http://*:8080'];
+  $listen = [$listen] unless ref $listen;
+  $daemon->listen($listen);
 }
 
 sub _heartbeat {
   my $self = shift;
 
-  # Poll
+  # Poll for heartbeats
   my $poll = $self->{_poll};
   $poll->poll(1);
-
-  # Readable
   return unless $poll->handles(POLLIN);
-
-  # Read
   return unless $self->{_reader}->sysread(my $chunk, 4194304);
 
-  # Parse
+  # Heartbeats
   while ($chunk =~ /(\d+)\n/g) {
     my $pid = $1;
-
-    # Heartbeat
     $self->{_workers}->{$pid}->{time} = time if $self->{_workers}->{$pid};
   }
 }
@@ -235,10 +185,8 @@ sub _heartbeat {
 sub _manage {
   my $self = shift;
 
-  # Config
-  my $c = $self->{_config};
-
   # Housekeeping
+  my $c = $self->{_config};
   if (!$self->{_done}) {
 
     # Spawn more workers
@@ -320,14 +268,12 @@ sub _manage {
 sub _pid {
   my $self = shift;
 
-  # PID file
+  # Check PID file
   my $file = $self->{_config}->{pid_file};
-
-  # Check
   return if -e $file;
   warn "PID $file\n" if DEBUG;
 
-  # Create
+  # Create one if it doesn't exist
   my $pid = IO::File->new($file, O_WRONLY | O_CREAT | O_EXCL, 0644)
     or croak qq/Can't create PID file "$file": $!/;
   print $pid $$;
@@ -365,30 +311,22 @@ sub _spawn {
 
   # Worker
   $ENV{HYPNOTOAD_WORKER} = 1;
-
-  # Daemon
   my $daemon = $self->{_daemon};
+  my $loop   = $daemon->ioloop;
+  my $c      = $self->{_config};
 
-  # Loop
-  my $loop = $daemon->ioloop;
-
-  # Config
-  my $c = $self->{_config};
-
-  # Lock file
+  # Prepare lock file
   my $file = $c->{lock_file};
   my $lock = IO::File->new("> $file")
     or croak qq/Can't open lock file "$file": $!/;
 
-  weaken $self;
-
   # Accept mutex
   $loop->on_lock(
     sub {
 
       # Blocking
       my $l;
-      if (my $blocking = $_[1]) {
+      if ($_[1]) {
         eval {
           local $SIG{ALRM} = sub { die "alarm\n" };
           my $old = alarm 1;
@@ -404,12 +342,13 @@ sub _spawn {
       # Non blocking
       else { $l = flock $lock, LOCK_EX | LOCK_NB }
 
-      return $l;
+      $l;
     }
   );
   $loop->on_unlock(sub { flock $lock, LOCK_UN });
 
   # Heartbeat
+  weaken $self;
   my $cb;
   $cb = sub {
     my $loop = shift;
@@ -451,7 +390,7 @@ Mojo::Server::Hypnotoad - ALL GLORY TO THE HYPNOTOAD!
   use Mojo::Server::Hypnotoad;
 
   my $toad = Mojo::Server::Hypnotoad->new;
-  $toad->run('myapp.pl', 'hypnotoad.conf');
+  $toad->run('./myapp.pl', './hypnotoad.conf');
 
 =head1 DESCRIPTION
 
@@ -460,6 +399,13 @@ io HTTP 1.1 and WebSocket server built around the very well tested and
 reliable L<Mojo::Server::Daemon> with C<IPv6>, C<TLS>, C<Bonjour>, C<epoll>,
 C<kqueue> and hot deployment support that just works.
 
+To start applications with it you can use the L<hypnotoad> script.
+
+  % hypnotoad myapp.pl
+
+For L<Mojolicious> and L<Mojolicious::Lite> applications it will default to
+C<production> mode.
+
 Optional modules L<IO::KQueue>, L<IO::Epoll>, L<IO::Socket::IP>,
 L<IO::Socket::SSL> and L<Net::Rendezvous::Publish> are supported
 transparently and used if installed.
@@ -664,7 +610,7 @@ Start server.
 You can set the C<HYPNOTOAD_DEBUG> environment variable to get some advanced
 diagnostics information printed to C<STDERR>.
 
-  MOJO_HYPNOTOAD_DEBUG=1
+  HYPNOTOAD_DEBUG=1
 
 =head1 SEE ALSO
 
@@ -0,0 +1,204 @@
+package Mojo::Server::Morbo;
+use Mojo::Base -base;
+
+use Carp 'croak';
+use Mojo::Home;
+use Mojo::Server::Daemon;
+use POSIX 'WNOHANG';
+
+use constant DEBUG => $ENV{MORBO_DEBUG} || 0;
+
+has listen => sub { [] };
+has watch  => sub { [qw/lib templates/] };
+
+# Cache stats
+my $STATS = {};
+
+# "All in all, this is one day Mittens the kitten won’t soon forget.
+#  Kittens give Morbo gas.
+#  In lighter news, the city of New New York is doomed.
+#  Blame rests with known human Professor Hubert Farnsworth and his tiny,
+#  inferior brain."
+sub check_file {
+  my ($self, $file) = @_;
+
+  # Check if modify time and/or size have changed
+  my ($size, $mtime) = (stat $file)[7, 9];
+  return unless defined $mtime;
+  my $stats = $STATS->{$file} ||= [$^T, $size];
+  return if $mtime <= $stats->[0] && $size == $stats->[1];
+  $STATS->{$file} = [$mtime, $size];
+
+  1;
+}
+
+sub run {
+  my ($self, $app) = @_;
+  warn "MANAGER STARTED $$\n" if DEBUG;
+
+  # Watch files and manage worker
+  $SIG{CHLD} = sub { $self->_reap };
+  $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = sub {
+    $self->{_done} = 1;
+    kill 'TERM', $self->{_running} if $self->{_running};
+  };
+  unshift @{$self->watch}, $app;
+  $self->{_modified} = 1;
+  $self->_manage while !$self->{_done} || $self->{_running};
+  exit 0;
+}
+
+# "And so with two weeks left in the campaign, the question on everyone’s
+#  mind is, who will be the president of Earth?
+#  Jack Johnson or bitter rival John Jackson.
+#  Two terrific candidates, Morbo?
+#  All humans are vermin in the eyes of Morbo!"
+sub _manage {
+  my $self = shift;
+
+  # Discover files
+  warn "DISCOVERING NEW FILES\n" if DEBUG;
+  my @files;
+  for my $watch (@{$self->watch}) {
+    if (-d $watch) {
+      my $home = Mojo::Home->new->parse($watch);
+      push @files, $home->rel_file($_) for @{$home->list_files};
+    }
+    elsif (-r $watch) { push @files, $watch }
+  }
+
+  # Check files
+  for my $file (@files) {
+    warn "CHECKING $file\n" if DEBUG;
+    next unless $self->check_file($file);
+    warn "MODIFIED $file\n" if DEBUG;
+    kill 'TERM', $self->{_running} if $self->{_running};
+    $self->{_modified} = 1;
+  }
+
+  # Housekeeping
+  $self->_reap;
+  delete $self->{_running} if $self->{_running} && !kill 0, $self->{_running};
+  $self->_spawn if !$self->{_running} && delete $self->{_modified};
+  sleep 1;
+}
+
+sub _reap {
+  my $self = shift;
+  while ((my $pid = waitpid -1, WNOHANG) > 0) {
+    warn "WORKER STOPPED $pid\n" if DEBUG;
+    delete $self->{_running};
+  }
+}
+
+# "Morbo cannot read his teleprompter.
+#  He forgot how you say that letter that looks like a man wearing a hat.
+#  It's a T. It goes 'tuh'.
+#  Hello, little man. I will destroy you!"
+sub _spawn {
+  my $self = shift;
+
+  # Fork
+  my $manager = $$;
+  $ENV{MORBO_REV}++;
+  croak "Can't fork: $!" unless defined(my $pid = fork);
+
+  # Manager
+  return $self->{_running} = $pid if $pid;
+
+  # Worker
+  warn "WORKER STARTED $$\n" if DEBUG;
+  $SIG{CHLD} = 'DEFAULT';
+  $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = sub { $self->{_done} = 1 };
+  my $daemon = Mojo::Server::Daemon->new;
+  $daemon->load_app($self->watch->[0]);
+  $daemon->silent(1) if $ENV{MORBO_REV} > 1;
+  $daemon->listen($self->listen) if @{$self->listen};
+  $daemon->prepare_ioloop;
+  my $loop = $daemon->ioloop;
+  $loop->recurring(
+    1 => sub { shift->stop if !kill(0, $manager) || $self->{_done} });
+  $loop->start;
+  exit 0;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojo::Server::Morbo - DOOOOOOOOOOOOOOOOOOM!
+
+=head1 SYNOPSIS
+
+  use Mojo::Server::Morbo;
+
+  my $morbo = Mojo::Server::Morbo->new;
+  $morbo->run('./myapp.pl');
+
+=head1 DESCRIPTION
+
+L<Mojo::Server::Morbo> is a full featured self-restart capable async io HTTP
+1.1 and WebSocket server built around the very well tested and reliable
+L<Mojo::Server::Daemon> with C<IPv6>, C<TLS>, C<Bonjour>, C<epoll> and
+C<kqueue> support.
+
+To start applications with it you can use the L<morbo> script.
+
+  % morbo myapp.pl
+
+Optional modules L<IO::KQueue>, L<IO::Epoll>, L<IO::Socket::IP>,
+L<IO::Socket::SSL> and L<Net::Rendezvous::Publish> are supported
+transparently and used if installed.
+
+Note that this module is EXPERIMENTAL and might change without warning!
+
+=head1 ATTRIBUTES
+
+L<Mojo::Server::Morbo> implements the following attributes.
+
+=head2 C<listen>
+
+  my $listen = $morbo->listen;
+  $morbo     = $morbo->listen(['http://*:3000']);
+
+List of ports and files to listen on, defaults to C<http://*:3000>.
+
+=head2 C<watch>
+
+  my $watch = $morbo->watch;
+  $morbo    = $morbo->watch(['/home/sri/myapp']);
+
+Files and directories to watch for changes, defaults to the application
+script as well as the C<lib> and C<templates> directories in the current
+working directory.
+
+=head1 METHODS
+
+L<Mojo::Server::Morbo> inherits all methods from L<Mojo::Base> and implements
+the following new ones.
+
+=head2 C<check_file>
+
+  $morbo->check_file('script/myapp');
+
+Check if file has been modified since last check.
+
+=head2 C<run>
+
+  $morbo->run('script/myapp');
+
+Start server.
+
+=head1 DEBUGGING
+
+You can set the C<MORBO_DEBUG> environment variable to get some advanced
+diagnostics information printed to C<STDERR>.
+
+  MORBO_DEBUG=1
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
+
+=cut
@@ -10,10 +10,9 @@ use constant CHUNK_SIZE => $ENV{MOJO_CHUNK_SIZE} || 131072;
 sub run {
   my ($self, $env) = @_;
 
+  # Environment
   my $tx  = $self->on_transaction->($self);
   my $req = $tx->req;
-
-  # Environment
   $req->parse($env);
 
   # Store connection information
@@ -34,10 +33,8 @@ sub run {
   # Handle
   $self->on_request->($self, $tx);
 
-  my $res = $tx->res;
-  my $code = $res->code || 404;
-
   # Fix headers
+  my $res = $tx->res;
   $res->fix_headers;
 
   # Response headers
@@ -55,7 +52,9 @@ sub run {
   # Finish transaction
   $tx->on_finish->($tx);
 
-  return [$code, \@headers, $body];
+  # PSGI response
+  my $code = $res->code || 404;
+  [$code, \@headers, $body];
 }
 
 package Mojo::Server::PSGI::_Handle;
@@ -87,7 +86,7 @@ sub getline {
     return $chunk;
   }
 
-  return;
+  undef;
 }
 
 1;
@@ -3,6 +3,7 @@ use Mojo::Base -base;
 
 use Carp 'croak';
 use Mojo::Loader;
+use Mojo::Util 'md5_sum';
 use Scalar::Util 'blessed';
 
 has app => sub {
@@ -30,13 +31,6 @@ has on_request => sub {
 has on_transaction => sub {
   sub {
     my $self = shift;
-
-    # Reload
-    if ($self->reload) {
-      if (my $e = Mojo::Loader->reload) { warn $e }
-      delete $self->{app};
-    }
-
     $self->app->on_transaction->($self->app);
   };
 };
@@ -46,21 +40,33 @@ has on_websocket => sub {
     $self->app->on_websocket->($self->app, @_)->server_handshake;
   };
 };
-has reload => sub { $ENV{MOJO_RELOAD} || 0 };
 
 sub load_app {
   my ($self, $file) = @_;
-  my $app;
+
+  # Cleanup environment
   local $ENV{MOJO_APP_LOADER} = 1;
-  unless ($app = do $file) {
-    die qq/Can't load application "$file": $@/ if $@;
-    die qq/Can't load application "$file": $!/ unless defined $app;
-    die qq/Can't load application' "$file".\n/ unless $app;
+  local $ENV{MOJO_APP};
+  local $ENV{MOJO_EXE};
+
+  # Try to load application from script into sandbox
+  my $class = 'Mojo::Server::SandBox::' . md5_sum($file . $$);
+  my $app;
+  die $@ unless eval <<EOF;
+package $class;
+{
+  unless (\$app = do \$file) {
+    die qq/Can't load application "\$file": \$@/ if \$@;
+    die qq/Can't load application "\$file": \$!/ unless defined \$app;
+    die qq/Can't load application' "\$file".\n/ unless \$app;
   }
+}
+1;
+EOF
   die qq/"$file" is not a valid application.\n/
     unless blessed $app && $app->isa('Mojo');
   $self->app($app);
-  return $app;
+  $app;
 }
 
 # "Are you saying you're never going to eat any animal again? What about
@@ -124,7 +130,7 @@ L<Mojo::HelloWorld>.
     my ($self, $tx) = @_;
   });
 
-Request callback.
+Callback to be invoked for requests that need a response.
 
 =head2 C<on_transaction>
 
@@ -134,7 +140,7 @@ Request callback.
     return Mojo::Transaction::HTTP->new;
   });
 
-Transaction builder callback.
+Callback to be invoked when a new transaction is needed.
 
 =head2 C<on_websocket>
 
@@ -143,14 +149,7 @@ Transaction builder callback.
     my ($self, $tx) = @_;
   });
 
-WebSocket handshake callback.
-
-=head2 C<reload>
-
-  my $reload = $server->reload;
-  $server    = $server->reload(1);
-
-Activate automatic reloading.
+Callback to be invoked for WebSocket handshakes.
 
 =head1 METHODS
 
@@ -20,7 +20,7 @@ has escape_mark     => '=';
 has expression_mark => '=';
 has line_start      => '%';
 has name            => 'template';
-has namespace       => 'Mojo::Template::Context';
+has namespace       => 'Mojo::Template::SandBox';
 has tag_start       => '<%';
 has tag_end         => '%>';
 has template        => '';
@@ -43,7 +43,7 @@ sub escape;
     $v = "$_[0]";
   }
   Mojo::Util::xml_escape $v;
-  return $v;
+  $v;
 };
 use strict; use warnings;
 EOF
@@ -72,7 +72,7 @@ sub build {
       if ($type eq 'cpen') {
 
         # End block
-        $lines[-1] .= 'return $_M }';
+        $lines[-1] .= 'return b($_M) }';
 
         # No following code
         my $next = $line->[$j + 3];
@@ -146,15 +146,14 @@ sub build {
   $self->code(join "\n", @lines);
   $self->tree([]);
 
-  return $self;
+  $self;
 }
 
 sub compile {
   my $self = shift;
 
-  return unless my $code = $self->code;
-
   # Compile
+  return unless my $code = $self->code;
   my $compiled = eval $code;
 
   # Use local stacktrace for compile exceptions
@@ -163,7 +162,7 @@ sub compile {
     if $@;
 
   $self->compiled($compiled);
-  return;
+  undef;
 }
 
 sub interpret {
@@ -172,8 +171,6 @@ sub interpret {
   # Compile
   unless ($self->compiled) {
     my $e = $self->compile;
-
-    # Exception
     return $e if ref $e;
   }
   my $compiled = $self->compiled;
@@ -192,7 +189,7 @@ sub interpret {
     Mojo::Exception->new($@, [$self->template], $self->name)->verbose(1)
     if $@;
 
-  return $output;
+  $output;
 }
 
 # "I am so smart! I am so smart! S-M-R-T! I mean S-M-A-R-T..."
@@ -291,8 +288,6 @@ sub parse {
 
     # Perl line
     if ($line =~ /$line_re/) {
-
-      # Token
       my @token = ();
 
       # Capture end
@@ -422,7 +417,7 @@ sub parse {
     push @{$self->tree}, \@token;
   }
 
-  return $self;
+  $self;
 }
 
 sub render {
@@ -440,7 +435,7 @@ sub render {
   return $e if $e;
 
   # Interpret
-  return $self->interpret(@_);
+  $self->interpret(@_);
 }
 
 sub render_file {
@@ -462,7 +457,7 @@ sub render_file {
   $tmpl = decode($self->encoding, $tmpl) if $self->encoding;
 
   # Render
-  return $self->render($tmpl, @_);
+  $self->render($tmpl, @_);
 }
 
 sub render_file_to_file {
@@ -472,12 +467,10 @@ sub render_file_to_file {
 
   # Render
   my $output = $self->render_file($spath, @_);
-
-  # Exception
   return $output if ref $output;
 
   # Write to file
-  return $self->_write_file($tpath, $output);
+  $self->_write_file($tpath, $output);
 }
 
 sub render_to_file {
@@ -487,12 +480,10 @@ sub render_to_file {
 
   # Render
   my $output = $self->render($tmpl, @_);
-
-  # Exception
   return $output if ref $output;
 
   # Write to file
-  return $self->_write_file($path, $output);
+  $self->_write_file($path, $output);
 }
 
 sub _trim_line {
@@ -523,7 +514,7 @@ sub _trim_line {
     return 1 if length $value;
   }
 
-  return;
+  undef;
 }
 
 sub _write_file {
@@ -539,7 +530,7 @@ sub _write_file {
   # Write to file
   $file->syswrite($output) or croak "Can't write to file '$path': $!";
 
-  return;
+  undef;
 }
 
 1;
@@ -794,7 +785,7 @@ Note that this method is attribute and might change without warning!
   my $namespace = $mt->namespace;
   $mt           = $mt->namespace('main');
 
-Namespace used to compile templates, defaults to C<Mojo::Template::Context>.
+Namespace used to compile templates, defaults to C<Mojo::Template::SandBox>.
 
 =head2 C<prepend>
 
@@ -13,17 +13,16 @@ has res => sub { Mojo::Message::Response->new };
 sub client_read {
   my ($self, $chunk) = @_;
 
-  my $req  = $self->req;
-  my $res  = $self->res;
-  my $read = length $chunk;
-
   # Preserve state
   my $preserved = $self->{_state};
 
   # Done
+  my $read = length $chunk;
   $self->{_state} = 'done' if $read == 0;
 
   # HEAD response
+  my $req = $self->req;
+  my $res = $self->res;
   if ($req->method =~ /^head$/i) {
     $res->parse_until_body($chunk);
     $self->{_state} = 'done' if $res->content->is_parsing_body;
@@ -46,20 +45,18 @@ sub client_read {
   # Check for errors
   $self->{_state} = 'done' if $self->error;
 
-  return $self;
+  $self;
 }
 
 sub client_write {
   my $self = shift;
 
-  my $chunk = '';
-  my $req   = $self->req;
-
   # Offsets
   $self->{_offset} ||= 0;
   $self->{_write}  ||= 0;
 
   # Writing
+  my $req = $self->req;
   unless ($self->{_state}) {
 
     # Connection header
@@ -77,6 +74,7 @@ sub client_write {
   }
 
   # Start line
+  my $chunk = '';
   if ($self->{_state} eq 'write_start_line') {
     my $buffer = $req->get_start_line_chunk($self->{_offset});
 
@@ -84,7 +82,6 @@ sub client_write {
     my $written = defined $buffer ? length $buffer : 0;
     $self->{_write}  = $self->{_write} - $written;
     $self->{_offset} = $self->{_offset} + $written;
-
     $chunk .= $buffer;
 
     # Done
@@ -103,7 +100,6 @@ sub client_write {
     my $written = defined $buffer ? length $buffer : 0;
     $self->{_write}  = $self->{_write} - $written;
     $self->{_offset} = $self->{_offset} + $written;
-
     $chunk .= $buffer;
 
     # Done
@@ -126,7 +122,6 @@ sub client_write {
     my $written = defined $buffer ? length $buffer : 0;
     $self->{_write}  = $self->{_write} - $written;
     $self->{_offset} = $self->{_offset} + $written;
-
     $chunk .= $buffer if defined $buffer;
 
     # End
@@ -140,7 +135,7 @@ sub client_write {
     $self->{_state} = 'read_response' if $self->{_write} <= 0;
   }
 
-  return $chunk;
+  $chunk;
 }
 
 sub keep_alive {
@@ -152,12 +147,11 @@ sub keep_alive {
     return $self;
   }
 
-  my $req = $self->req;
-  my $res = $self->res;
-
   # No keep alive for 0.9 and 1.0
+  my $req     = $self->req;
   my $version = $req->version;
   $self->{keep_alive} ||= 0 if $req->version eq '0.9' || $version eq '1.0';
+  my $res = $self->res;
   $version = $res->version;
   $self->{keep_alive} ||= 0 if $version eq '0.9' || $version eq '1.0';
 
@@ -177,7 +171,7 @@ sub keep_alive {
   # Default
   $self->{keep_alive} = 1 unless defined $self->{keep_alive};
 
-  return $self->{keep_alive};
+  $self->{keep_alive};
 }
 
 sub server_leftovers {
@@ -191,20 +185,19 @@ sub server_leftovers {
   # Done
   $req->{_state} = 'done';
 
-  return $leftovers;
+  $leftovers;
 }
 
 sub server_read {
   my ($self, $chunk) = @_;
 
-  my $req = $self->req;
-  my $res = $self->res;
-
   # Parse
+  my $req = $self->req;
   $req->parse($chunk) unless $req->error;
   $self->{_state} ||= 'read';
 
   # Parser error
+  my $res     = $self->res;
   my $handled = $self->{_handled};
   if ($req->error && !$handled) {
 
@@ -245,7 +238,7 @@ sub server_read {
     }
   }
 
-  return $self;
+  $self;
 }
 
 sub server_write {
@@ -259,10 +252,8 @@ sub server_write {
   $self->{_offset} ||= 0;
   $self->{_write}  ||= 0;
 
-  my $req = $self->req;
-  my $res = $self->res;
-
   # Writing
+  my $res = $self->res;
   if ($self->{_state} eq 'write') {
 
     # Connection header
@@ -285,7 +276,6 @@ sub server_write {
     my $written = defined $buffer ? length $buffer : 0;
     $self->{_write}  = $self->{_write} - $written;
     $self->{_offset} = $self->{_offset} + $written;
-
     $chunk .= $buffer;
 
     # Done
@@ -304,14 +294,13 @@ sub server_write {
     my $written = defined $buffer ? length $buffer : 0;
     $self->{_write}  = $self->{_write} - $written;
     $self->{_offset} = $self->{_offset} + $written;
-
     $chunk .= $buffer;
 
     # Done
     if ($self->{_write} <= 0) {
 
       # HEAD request
-      if ($req->method =~ /^head$/i) {
+      if ($self->req->method =~ /^head$/i) {
 
         # Don't send body if request method is HEAD
         $self->{_state} = 'done';
@@ -356,7 +345,6 @@ sub server_write {
       my $written = defined $buffer ? length $buffer : 0;
       $self->{_write}  = $self->{_write} - $written;
       $self->{_offset} = $self->{_offset} + $written;
-
       if (defined $buffer) {
         $chunk .= $buffer;
         delete $self->{_delay};
@@ -378,7 +366,7 @@ sub server_write {
     }
   }
 
-  return $chunk;
+  $chunk;
 }
 
 1;
@@ -409,26 +397,19 @@ described in RFC 2616.
 L<Mojo::Transaction::HTTP> inherits all attributes from L<Mojo::Transaction>
 and implements the following new ones.
 
-=head2 C<keep_alive>
-
-  my $keep_alive = $tx->keep_alive;
-  $tx            = $tx->keep_alive(1);
-
-Connection can be kept alive.
-
 =head2 C<on_upgrade>
 
   my $cb = $tx->on_upgrade;
   $tx    = $tx->on_upgrade(sub {...});
 
-WebSocket upgrade callback.
+Callback to be invoked for WebSocket upgrades.
 
 =head2 C<on_request>
 
   my $cb = $tx->on_request;
   $tx    = $tx->on_request(sub {...});
 
-Request callback.
+Callback to be invoked for requests.
 
 =head2 C<req>
 
@@ -461,6 +442,13 @@ Read and process client data.
 
 Write client data.
 
+=head2 C<keep_alive>
+
+  my $keep_alive = $tx->keep_alive;
+  $tx            = $tx->keep_alive(1);
+
+Connection can be kept alive.
+
 =head2 C<server_leftovers>
 
   my $leftovers = $tx->server_leftovers;
@@ -34,7 +34,7 @@ sub client_challenge {
   # WebSocket challenge
   my $solution = $self->_challenge($self->req->headers->sec_websocket_key);
   return unless $solution eq $self->res->headers->sec_websocket_accept;
-  return 1;
+  1;
 }
 
 sub client_close { shift->server_close(@_) }
@@ -55,7 +55,7 @@ sub client_handshake {
   b64_encode $key, '';
   $headers->sec_websocket_key($key) unless $headers->sec_websocket_key;
 
-  return $self;
+  $self;
 }
 
 sub client_read  { shift->server_read(@_) }
@@ -71,7 +71,7 @@ sub finish {
   # Finish after writing
   $self->{_finished} = 1;
 
-  return $self;
+  $self;
 }
 
 sub is_websocket {1}
@@ -86,41 +86,34 @@ sub res            { shift->handshake->res(@_) }
 sub resume {
   my $self = shift;
   $self->handshake->resume;
-  return $self;
+  $self;
 }
 
 sub send_message {
   my ($self, $message, $cb) = @_;
-
-  # Drain callback
   $self->{_drain} = $cb if $cb;
-
-  # Encode
   $message = '' unless defined $message;
   encode 'UTF-8', $message;
-
   $self->_send_frame(TEXT, $message);
 }
 
 sub server_handshake {
   my $self = shift;
 
-  my $req         = $self->req;
+  # Handshake
   my $res         = $self->res;
-  my $req_headers = $req->headers;
   my $res_headers = $res->headers;
-
-  # Handshake
   $res->code(101);
   $res_headers->upgrade('websocket');
   $res_headers->connection('Upgrade');
+  my $req_headers = $self->req->headers;
   my $protocol = $req_headers->sec_websocket_protocol || '';
   $protocol =~ /^\s*([^\,]+)/;
   $res_headers->sec_websocket_protocol($1) if $1;
   $res_headers->sec_websocket_accept(
     $self->_challenge($req_headers->sec_websocket_key));
 
-  return $self;
+  $self;
 }
 
 # "Being eaten by crocodile is just like going to sleep...
@@ -137,8 +130,6 @@ sub server_read {
 
   # Full frames
   while (my $frame = $self->_parse_frame) {
-
-    # Op
     my $op = $frame->[1] || CONTINUATION;
 
     # Ping
@@ -174,7 +165,7 @@ sub server_read {
   # Resume
   $self->on_resume->($self);
 
-  return $self;
+  $self;
 }
 
 sub server_write {
@@ -193,12 +184,11 @@ sub server_write {
   # Empty buffer
   my $write = $self->{_write};
   $self->{_write} = '';
-  return $write;
+  $write;
 }
 
 sub _build_frame {
   my ($self, $op, $payload) = @_;
-
   warn "BUILDING FRAME\n" if DEBUG;
 
   # Head
@@ -249,7 +239,7 @@ sub _build_frame {
   # Payload
   $frame .= $payload;
 
-  return $frame;
+  $frame;
 }
 
 sub _challenge {
@@ -264,12 +254,11 @@ sub _challenge {
   # Accept
   b64_encode $challenge, '';
 
-  return $challenge;
+  $challenge;
 }
 
 sub _parse_frame {
   my $self = shift;
-
   warn "PARSING FRAME\n" if DEBUG;
 
   # Head
@@ -333,11 +322,10 @@ sub _parse_frame {
     warn "UNMASKING PAYLOAD\n" if DEBUG;
     $payload = _xor_mask($payload, substr($payload, 0, 4, ''));
   }
-
   warn "PAYLOAD: $payload\n" if DEBUG;
   $self->{_read} = $buffer;
 
-  return [$fin, $op, $payload];
+  [$fin, $op, $payload];
 }
 
 sub _send_frame {
@@ -365,7 +353,7 @@ sub _xor_mask {
   $output .= $_ ^ $mask while length($_ = substr($input, 0, 512, '')) == 512;
   $output .= $_ ^ substr($mask, 0, length, '');
 
-  return $output;
+  $output;
 }
 
 1;
@@ -395,7 +383,8 @@ L<Mojo::Transaction> and implements the following new ones.
   my $handshake = $ws->handshake;
   $ws           = $ws->handshake(Mojo::Transaction::HTTP->new);
 
-The original handshake transaction.
+The original handshake transaction, defaults to a L<Mojo::Transaction::HTTP>
+object.
 
 =head2 C<masked>
 
@@ -416,7 +405,7 @@ Maximum WebSocket message size in bytes, defaults to C<262144>.
   my $cb = $ws->on_message;
   $ws    = $ws->on_message(sub {...});
 
-The callback that receives decoded messages one by one.
+Callback to be invoked for each decoded message.
 
   $ws->on_message(sub {
     my ($self, $message) = @_;
@@ -19,12 +19,12 @@ sub error {
   return $req->error if $req->error;
   my $res = $self->res;
   return $res->error if $res->error;
-  return;
+  undef;
 }
 
 sub is_done {
   return 1 if (shift->{_state} || '') eq 'done';
-  return;
+  undef;
 }
 
 sub is_websocket {0}
@@ -36,7 +36,7 @@ sub is_writing {
       || $state eq 'write_start_line'
       || $state eq 'write_headers'
       || $state eq 'write_body';
-  return;
+  undef;
 }
 
 sub remote_address {
@@ -67,7 +67,7 @@ sub remote_address {
   }
 
   # Get
-  return $self->{remote_address};
+  $self->{remote_address};
 }
 
 sub req { croak 'Method "req" not implemented by subclass' }
@@ -87,16 +87,13 @@ sub resume {
   # Callback
   $self->on_resume->($self);
 
-  return $self;
+  $self;
 }
 
 sub server_close {
   my $self = shift;
-
-  # Transaction finished
   $self->on_finish->($self);
-
-  return $self;
+  $self;
 }
 
 sub server_read  { croak 'Method "server_read" not implemented by subclass' }
@@ -105,7 +102,7 @@ sub server_write { croak 'Method "server_write" not implemented by subclass' }
 sub success {
   my $self = shift;
   return $self->res unless $self->error;
-  return;
+  undef;
 }
 
 1;
@@ -167,7 +164,7 @@ Local interface port.
   my $cb = $tx->on_finish;
   $tx    = $tx->on_finish(sub {...});
 
-Callback signaling that the transaction has been finished.
+Callback to be invoked when the transaction has been finished.
 
   $tx->on_finish(sub {
     my $self = shift;
@@ -0,0 +1,347 @@
+package Mojo::Transactor;
+use Mojo::Base -base;
+
+use Mojo::Asset::File;
+use Mojo::Asset::Memory;
+use Mojo::Content::MultiPart;
+use Mojo::Content::Single;
+use Mojo::Parameters;
+use Mojo::Transaction::HTTP;
+use Mojo::Transaction::WebSocket;
+use Mojo::URL;
+use Mojo::Util qw/encode url_escape/;
+
+# "I cheated the wrong way!
+#  I wrote the Lisa name and gave the Ralph answers!"
+sub form {
+  my $self = shift;
+  my $url  = shift;
+
+  # Callback
+  my $cb = pop @_ if ref $_[-1] && ref $_[-1] eq 'CODE';
+
+  # Form
+  my $encoding = shift;
+  my $form = ref $encoding ? $encoding : shift;
+  $encoding = undef if ref $encoding;
+
+  # Parameters
+  my $params = Mojo::Parameters->new;
+  $params->charset($encoding) if defined $encoding;
+  my $multipart;
+  for my $name (sort keys %$form) {
+
+    # Array
+    if (ref $form->{$name} eq 'ARRAY') {
+      $params->append($name, $_) for @{$form->{$name}};
+    }
+
+    # Hash
+    elsif (ref $form->{$name} eq 'HASH') {
+      my $hash = $form->{$name};
+
+      # Enforce "multipart/form-data"
+      $multipart = 1;
+
+      # File
+      if (my $file = $hash->{file}) {
+
+        # Upgrade
+        $file = $hash->{file} = Mojo::Asset::File->new(path => $file)
+          unless ref $file;
+
+        # Filename
+        $hash->{filename} ||= $file->path if $file->can('path');
+      }
+
+      # Memory
+      elsif (defined(my $content = delete $hash->{content})) {
+        $hash->{file} = Mojo::Asset::Memory->new->add_chunk($content);
+      }
+
+      $hash->{'Content-Type'} ||= 'application/octet-stream';
+      push @{$params->params}, $name, $hash;
+    }
+
+    # Single value
+    else { $params->append($name, $form->{$name}) }
+  }
+
+  # New transaction
+  my $tx      = $self->tx(POST => $url);
+  my $req     = $tx->req;
+  my $headers = $req->headers;
+  $headers->from_hash(ref $_[0] eq 'HASH' ? $_[0] : {@_});
+
+  # Multipart
+  $headers->content_type('multipart/form-data') if $multipart;
+  my $type = $headers->content_type || '';
+  if ($type eq 'multipart/form-data') {
+    my $form = $params->to_hash;
+
+    # Parts
+    my @parts;
+    foreach my $name (sort keys %$form) {
+      my $part = Mojo::Content::Single->new;
+      my $h    = $part->headers;
+      my $f    = $form->{$name};
+
+      # File
+      my $filename;
+      if (ref $f eq 'HASH') {
+        $filename = delete $f->{filename} || $name;
+        encode $encoding, $filename if $encoding;
+        url_escape $filename, $Mojo::URL::UNRESERVED;
+        $part->asset(delete $f->{file});
+        $h->from_hash($f);
+        push @parts, $part;
+      }
+
+      # Fields
+      else {
+        my $type = 'text/plain';
+        $type .= qq/;charset=$encoding/ if $encoding;
+        $h->content_type($type);
+
+        # Values
+        for my $value (ref $f ? @$f : ($f)) {
+          $part = Mojo::Content::Single->new(headers => $h);
+          encode $encoding, $value if $encoding;
+          $part->asset->add_chunk($value);
+          push @parts, $part;
+        }
+      }
+
+      # Content-Disposition
+      encode $encoding, $name if $encoding;
+      url_escape $name, $Mojo::URL::UNRESERVED;
+      my $disposition = qq/form-data; name="$name"/;
+      $disposition .= qq/; filename="$filename"/ if $filename;
+      $h->content_disposition($disposition);
+    }
+
+    # Multipart content
+    my $content = Mojo::Content::MultiPart->new;
+    $headers->content_type('multipart/form-data');
+    $content->headers($headers);
+    $content->parts(\@parts);
+
+    # Add content to transaction
+    $req->content($content);
+  }
+
+  # Urlencoded
+  else {
+    $headers->content_type('application/x-www-form-urlencoded');
+    $req->body($params->to_string);
+  }
+
+  return $tx unless wantarray;
+  $tx, $cb;
+}
+
+sub proxy_connect {
+  my ($self, $old) = @_;
+
+  # No proxy
+  my $req = $old->req;
+  return unless my $proxy = $req->proxy;
+
+  # WebSocket and/or HTTPS
+  my $url = $req->url;
+  return
+    unless ($req->headers->upgrade || '') eq 'websocket'
+    || ($url->scheme || '') eq 'https';
+
+  # CONNECT request
+  my $new = $self->tx(CONNECT => $url->clone);
+  $new->req->proxy($proxy);
+
+  $new;
+}
+
+sub redirect {
+  my ($self, $old) = @_;
+
+  # Code
+  my $res = $old->res;
+  return unless $res->is_status_class('300');
+  return if $res->code == 305;
+
+  # Location
+  return unless my $location = $res->headers->location;
+  $location = Mojo::URL->new($location);
+
+  # Fix broken location without authority and/or scheme
+  my $req = $old->req;
+  my $url = $req->url;
+  $location->authority($url->authority) unless $location->authority;
+  $location->scheme($url->scheme)       unless $location->scheme;
+
+  # Method
+  my $method = $req->method;
+  $method = 'GET' unless $method =~ /^GET|HEAD$/i;
+
+  # New transaction
+  my $new = Mojo::Transaction::HTTP->new;
+  $new->req->method($method)->url($location);
+  $new->previous($old);
+
+  $new;
+}
+
+sub tx {
+  my $self = shift;
+
+  # New transaction
+  my $tx  = Mojo::Transaction::HTTP->new;
+  my $req = $tx->req;
+  $req->method(shift);
+  my $url = shift;
+  $url = "http://$url" unless $url =~ /^\/|\:\/\//;
+  $req->url->parse($url);
+
+  # Callback
+  my $cb = pop @_ if ref $_[-1] && ref $_[-1] eq 'CODE';
+
+  # Body
+  $req->body(pop @_)
+    if @_ & 1 == 1 && ref $_[0] ne 'HASH' || ref $_[-2] eq 'HASH';
+
+  # Headers
+  $req->headers->from_hash(ref $_[0] eq 'HASH' ? $_[0] : {@_});
+
+  return $tx unless wantarray;
+  $tx, $cb;
+}
+
+# "She found my one weakness... that I'm weak!"
+sub websocket {
+  my $self = shift;
+
+  # New WebSocket
+  my ($tx, $cb) = $self->tx(GET => @_);
+  my $req = $tx->req;
+  my $url = $req->url;
+  my $abs = $url->to_abs;
+  if (my $scheme = $abs->scheme) {
+    $scheme = $scheme eq 'wss' ? 'https' : 'http';
+    $req->url($abs->scheme($scheme));
+  }
+
+  # Handshake
+  Mojo::Transaction::WebSocket->new(handshake => $tx, masked => 1)
+    ->client_handshake;
+
+  return $tx unless wantarray;
+  $tx, $cb;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojo::Transactor - Transaction Builder
+
+=head1 SYNOPSIS
+
+  use Mojo::Transactor;
+
+  my $t  = Mojo::Transactor->new;
+  my $tx = $t->tx(GET => 'http://mojolicio.us');
+
+=head1 DESCRIPTION
+
+L<Mojo::Transactor> is the request building framework used by
+L<Mojo::UserAgent>.
+Note that this module is EXPERIMENTAL and might change without warning!
+
+=head1 METHODS
+
+L<Mojo::Transactor> inherits all methods from L<Mojo::Base> and implements
+the following new ones.
+
+=head2 C<form>
+
+  my $tx = $t->form('http://kraih.com/foo' => {test => 123});
+  my $tx = $t->form(
+    'http://kraih.com/foo',
+    'UTF-8',
+    {test => 123}
+  );
+  my $tx = $t->form(
+    'http://kraih.com/foo',
+    {test => 123},
+    {Accept => '*/*'}
+  );
+  my $tx = $t->form(
+    'http://kraih.com/foo',
+    'UTF-8',
+    {test => 123},
+    {Accept => '*/*'}
+  );
+  my $tx = $t->form(
+    'http://kraih.com/foo',
+    {file => {file => '/foo/bar.txt'}}
+  );
+  my $tx = $t->form(
+    'http://kraih.com/foo',
+    {file => {content => 'lalala'}}
+  );
+  my $tx = $t->form(
+    'http://kraih.com/foo',
+    {myzip => {file => $asset, filename => 'foo.zip'}}
+  );
+
+Versatile L<Mojo::Transaction::HTTP> builder for form requests.
+
+  my $tx = $t->form('http://kraih.com/foo' => {test => 123});
+  $tx->res->body(sub { print $_[1] });
+  $ua->start($tx);
+
+=head2 C<proxy_connect>
+
+  my $tx = $t->proxy_connect($old);
+
+Build L<Mojo::Transaction::HTTP> proxy connect request for transaction.
+
+=head2 C<redirect>
+
+  my $tx = $t->redirect($old);
+
+Build L<Mojo::Transaction::HTTP> followup request for redirect response.
+
+=head2 C<tx>
+
+  my $tx = $t->tx(GET => 'mojolicio.us');
+  my $tx = $t->tx(POST => 'http://mojolicio.us');
+  my $tx = $t->tx(GET => 'http://kraih.com' => {Accept => '*/*'});
+  my $tx = $t->tx(
+    POST => 'http://kraih.com' => {{Accept => '*/*'} => 'Hi!'
+  );
+
+Versatile general purpose L<Mojo::Transaction::HTTP> builder for requests.
+
+  # Streaming response
+  my $tx = $t->tx(GET => 'http://mojolicio.us');
+  $tx->res->body(sub { print $_[1] });
+  $ua->start($tx);
+
+  # Custom socket
+  my $tx = $t->tx(GET => 'http://mojolicio.us');
+  $tx->connection($socket);
+  $ua->start($tx);
+
+=head2 C<websocket>
+
+  my $tx = $t->websocket('ws://localhost:3000');
+
+Versatile L<Mojo::Transaction::WebSocket> builder for WebSocket handshake
+requests.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
+
+=cut
@@ -40,7 +40,7 @@ our $IPV6_RE = qr/(?:
 sub new {
   my $self = shift->SUPER::new();
   $self->parse(@_);
-  return $self;
+  $self;
 }
 
 sub authority {
@@ -87,7 +87,7 @@ sub authority {
   $authority .= lc($host || '');
   $authority .= ":$port" if $port;
 
-  return $authority;
+  $authority;
 }
 
 sub clone {
@@ -101,7 +101,7 @@ sub clone {
   $clone->fragment($self->fragment);
   $clone->base($self->base->clone) if $self->{base};
 
-  return $clone;
+  $clone;
 }
 
 sub ihost {
@@ -138,23 +138,23 @@ sub ihost {
     push @encoded, $part;
   }
 
-  return join '.', @encoded;
+  join '.', @encoded;
 }
 
 sub is_abs {
   my $self = shift;
   return 1 if $self->scheme && $self->authority;
-  return;
+  undef;
 }
 
 sub is_ipv4 {
   return 1 if shift->host =~ $IPV4_RE;
-  return;
+  undef;
 }
 
 sub is_ipv6 {
   return 1 if shift->host =~ $IPV6_RE;
-  return;
+  undef;
 }
 
 sub parse {
@@ -164,14 +164,13 @@ sub parse {
   # Official regex
   my ($scheme, $authority, $path, $query, $fragment) = $url
     =~ m|(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?|;
-
   $self->scheme($scheme);
   $self->authority($authority);
   $self->path->parse($path);
-  $self->query->parse($query);
+  $self->query($query);
   $self->fragment($fragment);
 
-  return $self;
+  $self;
 }
 
 sub path {
@@ -203,7 +202,7 @@ sub path {
 
   # Get
   $self->{path} ||= Mojo::Path->new;
-  return $self->{path};
+  $self->{path};
 }
 
 sub query {
@@ -232,18 +231,15 @@ sub query {
       $q->append(%{$_[0]});
     }
 
-    # Replace with string or object
-    else {
-      $self->{query} =
-        !ref $_[0] ? Mojo::Parameters->new->append($_[0]) : $_[0];
-    }
+    # Replace with string
+    else { $self->{query} = Mojo::Parameters->new($_[0]) }
 
     return $self;
   }
 
   # Get
   $self->{query} ||= Mojo::Parameters->new;
-  return $self->{query};
+  $self->{query};
 }
 
 sub to_abs {
@@ -280,7 +276,7 @@ sub to_abs {
   $new->trailing_slash($old->trailing_slash);
   $abs->path($new);
 
-  return $abs;
+  $abs;
 }
 
 sub to_rel {
@@ -300,18 +296,16 @@ sub to_rel {
   $rel->scheme('');
   $rel->authority('');
 
+  # Characters after the right-most '/' need to go
   $rel->base($base->clone);
   my $splice = @{$base->path->parts};
-
-  # Characters after the right-most '/' need to go
   $splice -= 1 unless $base->path->trailing_slash;
-
   my $path = $rel->path->clone;
   splice @{$path->parts}, 0, $splice if $splice;
   $rel->path($path);
   $rel->path->leading_slash(0);
 
-  return $rel;
+  $rel;
 }
 
 # "Dad, what's a Muppet?
@@ -320,26 +314,25 @@ sub to_rel {
 sub to_string {
   my $self = shift;
 
+  # Scheme and authority
+  my $url       = '';
   my $scheme    = $self->scheme;
   my $authority = $self->authority;
-  my $path      = $self->path;
-  my $query     = $self->query;
-  my $url       = '';
-
-  # Scheme and authority
   if ($scheme && $authority) {
     $url .= lc "$scheme://";
     $url .= "$authority";
   }
 
   # Path
+  my $path  = $self->path;
   my $slash = $path->leading_slash;
   $path->leading_slash(0) if !$scheme && @{$self->base->path->parts};
   $url .= $path;
   $path->leading_slash($slash);
 
   # Query
-  $url .= "?$query" if @{$query->params};
+  my $query = join '', $self->query;
+  $url .= "?$query" if length $query;
 
   # Fragment
   if (my $fragment = $self->fragment) {
@@ -349,7 +342,7 @@ sub to_string {
     $url .= "#$fragment";
   }
 
-  return $url;
+  $url;
 }
 
 1;
@@ -14,10 +14,8 @@ has [qw/filename name/];
 #  This game makes no sense.
 #  Tell that to the good men who just lost their lives... SEMPER-FI!"
 sub move_to { shift->asset->move_to(@_) }
-
-sub size { shift->asset->size }
-
-sub slurp { shift->asset->slurp }
+sub size    { shift->asset->size }
+sub slurp   { shift->asset->slurp }
 
 1;
 __END__
@@ -47,8 +45,8 @@ L<Mojo::Upload> implements the following attributes.
   my $asset = $upload->asset;
   $upload   = $upload->asset(Mojo::Asset::File->new);
 
-Asset containing the uploaded data, usually a L<Mojo::Asset::Memory> or
-L<Mojo::Asset::File> object.
+Asset containing the uploaded data, defaults to a L<Mojo::Asset::File>
+object.
 
 =head2 C<filename>
 
@@ -62,7 +60,7 @@ Name of the uploaded file.
   my $headers = $upload->headers;
   $upload     = $upload->headers(Mojo::Headers->new);
 
-Headers for upload, usually a L<Mojo::Headers> object.
+Headers for upload, defaults to a L<Mojo::Headers> object.
 
 =head2 C<name>
 
@@ -2,19 +2,12 @@ package Mojo::UserAgent;
 use Mojo::Base -base;
 
 use Carp 'croak';
-use Mojo::Asset::File;
-use Mojo::Asset::Memory;
-use Mojo::Content::MultiPart;
-use Mojo::Content::Single;
 use Mojo::CookieJar;
 use Mojo::IOLoop;
 use Mojo::Log;
-use Mojo::Parameters;
 use Mojo::Server::Daemon;
-use Mojo::Transaction::HTTP;
 use Mojo::Transaction::WebSocket;
-use Mojo::Util qw/encode url_escape/;
-use Mojo::URL;
+use Mojo::Transactor;
 use Scalar::Util 'weaken';
 
 use constant DEBUG => $ENV{MOJO_USERAGENT_DEBUG} || 0;
@@ -30,215 +23,46 @@ has log                => sub { Mojo::Log->new };
 has max_connections    => 5;
 has max_redirects      => sub { $ENV{MOJO_MAX_REDIRECTS} || 0 };
 has name               => 'Mojolicious (Perl)';
-has websocket_timeout  => 300;
+has transactor => sub { Mojo::Transactor->new };
+has websocket_timeout => 300;
 
 # Make sure we leave a clean ioloop behind
 sub DESTROY { shift->_cleanup }
 
 # "Ah, alcohol and night-swimming. It's a winning combination."
-sub build_form_tx {
-  my $self = shift;
-  my $url  = shift;
-
-  # Callback
-  my $cb = pop @_ if ref $_[-1] && ref $_[-1] eq 'CODE';
-
-  # Form
-  my $encoding = shift;
-  my $form = ref $encoding ? $encoding : shift;
-  $encoding = undef if ref $encoding;
-
-  # Parameters
-  my $params = Mojo::Parameters->new;
-  $params->charset($encoding) if defined $encoding;
-  my $multipart;
-  for my $name (sort keys %$form) {
-
-    # Array
-    if (ref $form->{$name} eq 'ARRAY') {
-      $params->append($name, $_) for @{$form->{$name}};
-    }
-
-    # Hash
-    elsif (ref $form->{$name} eq 'HASH') {
-      my $hash = $form->{$name};
-
-      # Enforce "multipart/form-data"
-      $multipart = 1;
-
-      # File
-      if (my $file = $hash->{file}) {
-
-        # Upgrade
-        $file = $hash->{file} = Mojo::Asset::File->new(path => $file)
-          unless ref $file;
-
-        # Filename
-        $hash->{filename} ||= $file->path if $file->can('path');
-      }
-
-      # Memory
-      elsif (defined(my $content = delete $hash->{content})) {
-        $hash->{file} = Mojo::Asset::Memory->new->add_chunk($content);
-      }
-
-      $hash->{'Content-Type'} ||= 'application/octet-stream';
-      push @{$params->params}, $name, $hash;
-    }
-
-    # Single value
-    else { $params->append($name, $form->{$name}) }
-  }
-
-  # New transaction
-  my $tx      = $self->build_tx(POST => $url);
-  my $req     = $tx->req;
-  my $headers = $req->headers;
-  $headers->from_hash(ref $_[0] eq 'HASH' ? $_[0] : {@_});
-
-  # Multipart
-  $headers->content_type('multipart/form-data') if $multipart;
-  my $type = $headers->content_type || '';
-  if ($type eq 'multipart/form-data') {
-    my $form = $params->to_hash;
-
-    # Parts
-    my @parts;
-    foreach my $name (sort keys %$form) {
-      my $part = Mojo::Content::Single->new;
-      my $h    = $part->headers;
-      my $f    = $form->{$name};
-
-      # File
-      my $filename;
-      if (ref $f eq 'HASH') {
-        $filename = delete $f->{filename} || $name;
-        encode $encoding, $filename if $encoding;
-        url_escape $filename, $Mojo::URL::UNRESERVED;
-        $part->asset(delete $f->{file});
-        $h->from_hash($f);
-        push @parts, $part;
-      }
-
-      # Fields
-      else {
-        my $type = 'text/plain';
-        $type .= qq/;charset=$encoding/ if $encoding;
-        $h->content_type($type);
-
-        # Values
-        for my $value (ref $f ? @$f : ($f)) {
-          $part = Mojo::Content::Single->new(headers => $h);
-          encode $encoding, $value if $encoding;
-          $part->asset->add_chunk($value);
-          push @parts, $part;
-        }
-      }
-
-      # Content-Disposition
-      encode $encoding, $name if $encoding;
-      url_escape $name, $Mojo::URL::UNRESERVED;
-      my $disposition = qq/form-data; name="$name"/;
-      $disposition .= qq/; filename="$filename"/ if $filename;
-      $h->content_disposition($disposition);
-    }
-
-    # Multipart content
-    my $content = Mojo::Content::MultiPart->new;
-    $headers->content_type('multipart/form-data');
-    $content->headers($headers);
-    $content->parts(\@parts);
-
-    # Add content to transaction
-    $req->content($content);
-  }
-
-  # Urlencoded
-  else {
-    $headers->content_type('application/x-www-form-urlencoded');
-    $req->body($params->to_string);
-  }
-
-  return $tx unless wantarray;
-  return $tx, $cb;
-}
-
-# "Homer, it's easy to criticize.
-#  Fun, too."
-sub build_tx {
-  my $self = shift;
-
-  # New transaction
-  my $tx  = Mojo::Transaction::HTTP->new;
-  my $req = $tx->req;
-  $req->method(shift);
-  my $url = shift;
-  $url = "http://$url" unless $url =~ /^\/|\:\/\//;
-  $req->url->parse($url);
-
-  # Callback
-  my $cb = pop @_ if ref $_[-1] && ref $_[-1] eq 'CODE';
-
-  # Body
-  $req->body(pop @_)
-    if @_ & 1 == 1 && ref $_[0] ne 'HASH' || ref $_[-2] eq 'HASH';
-
-  # Headers
-  $req->headers->from_hash(ref $_[0] eq 'HASH' ? $_[0] : {@_});
-
-  return $tx unless wantarray;
-  return $tx, $cb;
-}
-
-sub build_websocket_tx {
-  my $self = shift;
-
-  # New WebSocket
-  my ($tx, $cb) = $self->build_tx(GET => @_);
-  my $req = $tx->req;
-  my $url = $req->url;
-  my $abs = $url->to_abs;
-  if (my $scheme = $abs->scheme) {
-    $scheme = $scheme eq 'wss' ? 'https' : 'http';
-    $req->url($abs->scheme($scheme));
-  }
-
-  # Handshake
-  Mojo::Transaction::WebSocket->new(handshake => $tx, masked => 1)
-    ->client_handshake;
-
-  return $tx unless wantarray;
-  return $tx, $cb;
-}
+sub build_form_tx      { shift->transactor->form(@_) }
+sub build_tx           { shift->transactor->tx(@_) }
+sub build_websocket_tx { shift->transactor->websocket(@_) }
 
 # "The only thing I asked you to do for this party was put on clothes,
 #  and you didn't do it."
 sub delete {
   my $self = shift;
-  return $self->start($self->build_tx('DELETE', @_));
+  $self->start($self->build_tx('DELETE', @_));
 }
 
 sub detect_proxy {
   my $self = shift;
 
+  # Uppercase gets priority
   $self->http_proxy($ENV{HTTP_PROXY}   || $ENV{http_proxy});
   $self->https_proxy($ENV{HTTPS_PROXY} || $ENV{https_proxy});
   if (my $no = $ENV{NO_PROXY} || $ENV{no_proxy}) {
     $self->no_proxy([split /,/, $no]);
   }
 
-  return $self;
+  $self;
 }
 
 # "'What are you lookin at?' - the innocent words of a drunken child."
 sub get {
   my $self = shift;
-  return $self->start($self->build_tx('GET', @_));
+  $self->start($self->build_tx('GET', @_));
 }
 
 sub head {
   my $self = shift;
-  return $self->start($self->build_tx('HEAD', @_));
+  $self->start($self->build_tx('HEAD', @_));
 }
 
 sub need_proxy {
@@ -251,19 +75,19 @@ sub need_proxy {
   $host =~ /\Q$_\E$/ and return for @$no;
 
   # Proxy needed
-  return 1;
+  1;
 }
 
 # "Olive oil? Asparagus? If your mother wasn't so fancy,
 #  we could just shop at the gas station like normal people."
 sub post {
   my $self = shift;
-  return $self->start($self->build_tx('POST', @_));
+  $self->start($self->build_tx('POST', @_));
 }
 
 sub post_form {
   my $self = shift;
-  return $self->start($self->build_form_tx(@_));
+  $self->start($self->build_form_tx(@_));
 }
 
 # "And I gave that man directions, even though I didn't know the way,
@@ -273,13 +97,11 @@ sub put {
   $self->start($self->build_tx('PUT', @_));
 }
 
-# "Wow, Barney. You brought a whole beer keg.
-#  Yeah... where do I fill it up?"
 sub start {
   my ($self, $tx, $cb) = @_;
 
-  # Default loop
-  $self->{_loop} ||= $self->ioloop;
+  # Blocking loop
+  my $loop = $self->{_loop} ||= $self->ioloop;
 
   # Non-blocking
   if ($cb) {
@@ -289,7 +111,7 @@ sub start {
     $self->_switch_non_blocking unless $self->{_nb};
 
     # Start
-    return $self->_start_tx($tx, $cb);
+    return $self->_start($tx, $cb);
   }
 
   # Switch to blocking
@@ -297,43 +119,41 @@ sub start {
   $self->_switch_blocking if $self->{_nb};
 
   # Quick start
-  $self->_start_tx($tx, sub { $tx = $_[1] });
+  $self->_start($tx, sub { $tx = $_[1] });
 
   # Start loop
-  $self->{_loop}->start;
+  $loop->start;
 
   # Cleanup
-  $self->{_loop}->one_tick(0);
+  $loop->one_tick(0);
 
-  return $tx;
+  $tx;
 }
 
 # "It's like my dad always said: eventually, everybody gets shot."
 sub test_server {
   my ($self, $protocol) = @_;
 
-  # Server
+  # Start test server
   unless ($self->{_port}) {
     my $loop = $self->{_loop} || $self->ioloop;
-
     my $server = $self->{_server} =
       Mojo::Server::Daemon->new(ioloop => $loop, silent => 1);
     my $port = $self->{_port} = $loop->generate_port;
     die "Couldn't find a free TCP port for testing.\n" unless $port;
-
     $self->{_protocol} = $protocol ||= 'http';
     $server->listen(["$protocol://*:$port"]);
     $server->prepare_ioloop;
   }
 
-  # Application
+  # Prepare application for testing
   my $server = $self->{_server};
   delete $server->{app};
   my $app = $self->app;
   ref $app ? $server->app($app) : $server->app_class($app);
   $self->log($server->app->log);
 
-  return $self->{_port};
+  $self->{_port};
 }
 
 # "Are we there yet?
@@ -348,6 +168,8 @@ sub websocket {
   $self->start($self->build_websocket_tx(@_));
 }
 
+# "Homer, it's easy to criticize.
+#  Fun, too."
 sub _cache {
   my ($self, $name, $id) = @_;
 
@@ -388,7 +210,7 @@ sub _cache {
   }
   $self->{_cache} = \@cache;
 
-  return $result;
+  $result;
 }
 
 sub _cleanup {
@@ -417,15 +239,12 @@ sub _close { shift->_handle(pop, 1) }
 #  Uh, second word, chief."
 sub _connect {
   my ($self, $tx, $cb) = @_;
-  my $loop = $self->{_loop};
-
-  # Info
-  my $id = $tx->connection;
-  my ($scheme, $address, $port) = $self->_tx_info($tx);
-
-  weaken $self;
 
   # Keep alive connection
+  weaken $self;
+  my $loop = $self->{_loop};
+  my $id   = $tx->connection;
+  my ($scheme, $address, $port) = $self->_info($tx);
   $id ||= $self->_cache("$scheme:$address:$port");
   if ($id && !ref $id) {
     warn "KEEP ALIVE CONNECTION ($scheme:$address:$port)\n" if DEBUG;
@@ -441,7 +260,7 @@ sub _connect {
     unless (($tx->req->method || '') eq 'CONNECT') {
 
       # CONNECT request to proxy required
-      return if $self->_connect_proxy($tx, $cb);
+      return if $self->_proxy_connect($tx, $cb);
     }
 
     # Connect
@@ -463,64 +282,7 @@ sub _connect {
   $loop->on_error($id => sub { $self->_error(@_) });
   $loop->on_read($id => sub { $self->_read(@_) });
 
-  return $id;
-}
-
-# "Hey, Weener Boy... where do you think you're going?"
-sub _connect_proxy {
-  my ($self, $old, $cb) = @_;
-
-  my $req = $old->req;
-  my $url = $req->url;
-
-  # No proxy
-  return unless my $proxy = $req->proxy;
-
-  # WebSocket and/or HTTPS
-  return
-    unless ($req->headers->upgrade || '') eq 'websocket'
-    || ($url->scheme || '') eq 'https';
-
-  # CONNECT request
-  my $new = $self->build_tx(CONNECT => $url->clone);
-  $new->req->proxy($proxy);
-
-  # Start CONNECT request
-  $self->_start_tx(
-    $new => sub {
-      my ($self, $tx) = @_;
-
-      # CONNECT failed
-      unless (($tx->res->code || '') eq '200') {
-        $old->req->error('Proxy connection failed.');
-        $self->_finish_tx($old, $cb);
-        return;
-      }
-
-      # TLS upgrade
-      if ($tx->req->url->scheme eq 'https') {
-
-        # Connection from keep alive cache
-        return unless my $old_id = $tx->connection;
-
-        # Start TLS
-        my $new_id = $self->{_loop}->start_tls($old_id);
-
-        # Cleanup
-        $old->req->proxy(undef);
-        delete $self->{_cs}->{$old_id};
-        $tx->connection($new_id);
-      }
-
-      # Share connection
-      $old->connection($tx->connection);
-
-      # Start real transaction
-      $self->_start_tx($old, $cb);
-    }
-  );
-
-  return 1;
+  $id;
 }
 
 # "I don't mind being called a liar when I'm lying, or about to lie,
@@ -528,11 +290,10 @@ sub _connect_proxy {
 sub _connected {
   my ($self, $id) = @_;
 
+  # Store connection information in transaction
   my $loop = $self->{_loop};
   my $tx   = $self->{_cs}->{$id}->{tx};
   $tx->connection($id);
-
-  # Store connection information in transaction
   my $local = $loop->local_info($id);
   $tx->local_address($local->{address});
   $tx->local_port($local->{port});
@@ -560,7 +321,7 @@ sub _drop {
   if (!$close && $tx && $tx->keep_alive && !$tx->error) {
 
     # Keep non-CONNECTed connection alive
-    $self->_cache(join(':', $self->_tx_info($tx)), $id)
+    $self->_cache(join(':', $self->_info($tx)), $id)
       unless (($tx->req->method || '') =~ /^connect$/i
       && ($tx->res->code || '') eq '200');
 
@@ -576,25 +337,33 @@ sub _drop {
 sub _error {
   my ($self, $loop, $id, $error) = @_;
 
-  # Transaction
+  # Store error in response
   if (my $tx = $self->{_cs}->{$id}->{tx}) { $tx->res->error($error) }
 
-  # Log
+  # Log error
   $self->log->error($error);
 
-  # Finished
+  # Finish connection
   $self->_handle($id, $error);
 }
 
 # "Oh, I'm in no condition to drive. Wait a minute.
 #  I don't have to listen to myself. I'm drunk."
-sub _finish_tx {
-  my ($self, $tx, $cb) = @_;
+sub _finish {
+  my ($self, $tx, $cb, $close) = @_;
 
-  # 400/500
+  # Common errors
   my $res = $tx->res;
-  $res->error($res->message, $res->code)
-    if $res->is_status_class(400) || $res->is_status_class(500);
+  unless ($res->error) {
+
+    # Premature connection close
+    if ($close && !$res->code) { $res->error('Premature connection close.') }
+
+    # 400/500
+    elsif ($res->is_status_class(400) || $res->is_status_class(500)) {
+      $res->error($res->message, $res->code);
+    }
+  }
 
   # Callback
   return unless $cb;
@@ -606,10 +375,9 @@ sub _finish_tx {
 sub _handle {
   my ($self, $id, $close) = @_;
 
+  # WebSocket
   my $c   = $self->{_cs}->{$id};
   my $old = $c->{tx};
-
-  # WebSocket
   if ($old && $old->is_websocket) {
 
     # Finish transaction
@@ -622,17 +390,17 @@ sub _handle {
   # Upgrade connection to WebSocket
   elsif ($old && (my $new = $self->_upgrade($id))) {
 
-    # Finish
-    $self->_finish_tx($new, $c->{cb});
+    # Finish transaction
+    $self->_finish($new, $c->{cb});
 
-    # Leftovers
+    # Parse leftovers
     $new->client_read($old->res->leftovers);
   }
 
   # Normal connection
   else {
 
-    # Cleanup
+    # Cleanup connection
     $self->_drop($id, $close);
 
     # Idle connection
@@ -645,7 +413,7 @@ sub _handle {
     $self->{_processing} -= 1;
 
     # Redirect or callback
-    $self->_finish_tx($new || $old, $c->{cb})
+    $self->_finish($new || $old, $c->{cb}, $close)
       unless $self->_redirect($c, $old);
   }
 
@@ -653,6 +421,72 @@ sub _handle {
   $self->{_loop}->stop if !$self->{_nb} && !$self->{_processing};
 }
 
+sub _info {
+  my ($self, $tx) = @_;
+
+  # Proxy info
+  my $req    = $tx->req;
+  my $url    = $req->url;
+  my $scheme = $url->scheme || 'http';
+  my $host   = $url->ihost;
+  my $port   = $url->port;
+  if (my $proxy = $req->proxy) {
+    $scheme = $proxy->scheme;
+    $host   = $proxy->ihost;
+    $port   = $proxy->port;
+  }
+
+  # Default port
+  $port ||= $scheme eq 'https' ? 443 : 80;
+
+  ($scheme, $host, $port);
+}
+
+# "Hey, Weener Boy... where do you think you're going?"
+sub _proxy_connect {
+  my ($self, $old, $cb) = @_;
+
+  # CONNECT request
+  return unless my $new = $self->transactor->proxy_connect($old);
+
+  # Start CONNECT request
+  $self->_start(
+    $new => sub {
+      my ($self, $tx) = @_;
+
+      # CONNECT failed
+      unless (($tx->res->code || '') eq '200') {
+        $old->req->error('Proxy connection failed.');
+        $self->_finish($old, $cb);
+        return;
+      }
+
+      # TLS upgrade
+      if ($tx->req->url->scheme eq 'https') {
+
+        # Connection from keep alive cache
+        return unless my $old_id = $tx->connection;
+
+        # Start TLS
+        my $new_id = $self->{_loop}->start_tls($old_id);
+
+        # Cleanup
+        $old->req->proxy(undef);
+        delete $self->{_cs}->{$old_id};
+        $tx->connection($new_id);
+      }
+
+      # Share connection
+      $old->connection($tx->connection);
+
+      # Start real transaction
+      $self->_start($old, $cb);
+    }
+  );
+
+  1;
+}
+
 # "Have you ever seen that Blue Man Group? Total ripoff of the Smurfs.
 #  And the Smurfs, well, they SUCK."
 sub _read {
@@ -682,47 +516,26 @@ sub _read {
 sub _redirect {
   my ($self, $c, $old) = @_;
 
-  # Code
-  my $res = $old->res;
-  return unless $res->is_status_class('300');
-  return if $res->code == 305;
-
-  # Location
-  return unless my $location = $res->headers->location;
-  $location = Mojo::URL->new($location);
-
-  # Fix broken location without authority and/or scheme
-  my $req = $old->req;
-  my $url = $req->url;
-  $location->authority($url->authority) unless $location->authority;
-  $location->scheme($url->scheme)       unless $location->scheme;
-
-  # Method
-  my $method = $req->method;
-  $method = 'GET' unless $method =~ /^GET|HEAD$/i;
+  # Build followup transaction
+  return unless my $new = $self->transactor->redirect($old);
 
   # Max redirects
-  my $r = $c->{redirects} || 0;
+  my $redirects = $c->{redirects} || 0;
   my $max = $self->max_redirects;
-  return unless $r < $max;
-
-  # New transaction
-  my $new = Mojo::Transaction::HTTP->new;
-  $new->req->method($method)->url($location);
-  $new->previous($old);
+  return unless $redirects < $max;
 
   # Start redirected request
-  return 1 unless my $new_id = $self->_start_tx($new, $c->{cb});
+  return 1 unless my $new_id = $self->_start($new, $c->{cb});
 
   # Create new connection
-  $self->{_cs}->{$new_id}->{redirects} = $r + 1;
+  $self->{_cs}->{$new_id}->{redirects} = $redirects + 1;
 
   # Redirecting
-  return 1;
+  1;
 }
 
-# "It's greeat! We can do *anything* now that Science has invented Magic."
-sub _start_tx {
+# "It's great! We can do *anything* now that Science has invented Magic."
+sub _start {
   my ($self, $tx, $cb) = @_;
 
   # Embedded server
@@ -742,11 +555,10 @@ sub _start_tx {
   # Detect proxy
   $self->detect_proxy if $ENV{MOJO_PROXY};
 
+  # Proxy
   my $req    = $tx->req;
   my $url    = $req->url;
   my $scheme = $url->scheme || '';
-
-  # Proxy
   if ($self->need_proxy($url->host)) {
 
     # HTTP proxy
@@ -781,7 +593,7 @@ sub _start_tx {
   $self->{_processing} ||= 0;
   $self->{_processing} += 1;
 
-  return $id;
+  $id;
 }
 
 sub _switch_blocking {
@@ -791,9 +603,8 @@ sub _switch_blocking {
   croak 'Non-blocking requests in progress' if $self->{_processing};
   warn "SWITCHING TO BLOCKING MODE\n" if DEBUG;
 
-  $self->_cleanup;
-
   # Normal loop
+  $self->_cleanup;
   $self->{_loop} = $self->ioloop;
   $self->{_nb}   = 0;
 }
@@ -805,43 +616,19 @@ sub _switch_non_blocking {
   croak 'Blocking request in progress' if $self->{_processing};
   warn "SWITCHING TO NON-BLOCKING MODE\n" if DEBUG;
 
-  $self->_cleanup;
-
   # Global loop
+  $self->_cleanup;
   $self->{_loop} = Mojo::IOLoop->singleton;
   $self->{_nb}   = 1;
 }
 
-sub _tx_info {
-  my ($self, $tx) = @_;
-
-  my $req    = $tx->req;
-  my $url    = $req->url;
-  my $scheme = $url->scheme || 'http';
-  my $host   = $url->ihost;
-  my $port   = $url->port;
-
-  # Proxy info
-  if (my $proxy = $req->proxy) {
-    $scheme = $proxy->scheme;
-    $host   = $proxy->ihost;
-    $port   = $proxy->port;
-  }
-
-  # Default port
-  $port ||= $scheme eq 'https' ? 443 : 80;
-
-  return ($scheme, $host, $port);
-}
-
 # "Once the government approves something, it's no longer immoral!"
 sub _upgrade {
   my ($self, $id) = @_;
 
+  # No upgrade request
   my $c   = $self->{_cs}->{$id};
   my $old = $c->{tx};
-
-  # No upgrade request
   return unless $old->req->headers->upgrade;
 
   # Handshake failed
@@ -864,7 +651,7 @@ sub _upgrade {
   weaken $self;
   $new->on_resume(sub { $self->_write($id) });
 
-  return $new;
+  $new;
 }
 
 # "Oh well. At least we'll die doing what we love: inhaling molten rock."
@@ -1077,6 +864,14 @@ redirects.
     $tx->req->headers->header('X-Bender', 'Bite my shiny metal ass!');
   });
 
+=head2 C<transactor>
+
+  my $t = $ua->transactor;
+  $ua   = $ua->transactor(Mojo::Transactor->new);
+
+Transaction builder, by default a L<Mojo::Transactor> object.
+Note that this attribute is EXPERIMENTAL and might change without warning!
+
 =head2 C<websocket_timeout>
 
   my $websocket_timeout = $ua->websocket_timeout;
@@ -1093,69 +888,20 @@ following new ones.
 =head2 C<build_form_tx>
 
   my $tx = $ua->build_form_tx('http://kraih.com/foo' => {test => 123});
-  my $tx = $ua->build_form_tx(
-    'http://kraih.com/foo',
-    'UTF-8',
-    {test => 123}
-  );
-  my $tx = $ua->build_form_tx(
-    'http://kraih.com/foo',
-    {test => 123},
-    {Accept => '*/*'}
-  );
-  my $tx = $ua->build_form_tx(
-    'http://kraih.com/foo',
-    'UTF-8',
-    {test => 123},
-    {Accept => '*/*'}
-  );
-  my $tx = $ua->build_form_tx(
-    'http://kraih.com/foo',
-    {file => {file => '/foo/bar.txt'}}
-  );
-  my $tx = $ua->build_form_tx(
-    'http://kraih.com/foo',
-    {file => {content => 'lalala'}}
-  );
-  my $tx = $ua->build_form_tx(
-    'http://kraih.com/foo',
-    {myzip => {file => $asset, filename => 'foo.zip'}}
-  );
-
-Versatile L<Mojo::Transaction::HTTP> builder for forms.
 
-  my $tx = $ua->build_form_tx('http://kraih.com/foo' => {test => 123});
-  $tx->res->body(sub { print $_[1] });
-  $ua->start($tx);
+Alias for the C<form> method in L<Mojo::Transactor>.
 
 =head2 C<build_tx>
 
   my $tx = $ua->build_tx(GET => 'mojolicio.us');
-  my $tx = $ua->build_tx(POST => 'http://mojolicio.us');
-  my $tx = $ua->build_tx(GET => 'http://kraih.com' => {Accept => '*/*'});
-  my $tx = $ua->build_tx(
-    POST => 'http://kraih.com' => {{Accept => '*/*'} => 'Hi!'
-  );
 
-Versatile general purpose L<Mojo::Transaction::HTTP> builder.
-
-  # Streaming response
-  my $tx = $ua->build_tx(GET => 'http://mojolicio.us');
-  $tx->res->body(sub { print $_[1] });
-  $ua->start($tx);
-
-  # Custom socket
-  my $tx = $ua->build_tx(GET => 'http://mojolicio.us');
-  $tx->connection($socket);
-  $ua->start($tx);
+Alias for the C<tx> method in L<Mojo::Transactor>.
 
 =head2 C<build_websocket_tx>
 
   my $tx = $ua->build_websocket_tx('ws://localhost:3000');
 
-Versatile L<Mojo::Transaction::HTTP> builder for WebSocket handshakes.
-An upgrade to L<Mojo::Transaction::WebSocket> will happen automatically after
-a successful handshake is performed.
+Alias for the C<websocket> method in L<Mojo::Transactor>.
 
 =head2 C<delete>
 
@@ -1323,10 +1069,13 @@ Note that this method is EXPERIMENTAL and might change without warning!
 
 Open a non-blocking WebSocket connection with transparent handshake.
 
-  $ua->websocket('ws://localhost:3000' => sub {
+  $ua->websocket('ws://localhost:3000/echo' => sub {
     my $tx = pop;
-    $tx->on_finish(sub { Mojo::IOLoop->stop });
-    $tx->on_message(sub { say pop });
+    $tx->on_finish(sub  { Mojo::IOLoop->stop });
+    $tx->on_message(sub {
+      my ($tx, $message) = @_;
+      print "$message\n";
+    });
     $tx->send_message('Hi!');
   });
   Mojo::IOLoop->start;
@@ -376,7 +376,7 @@ sub get_line {
   my $line = substr $_[0], 0, $pos + 1, '';
   $line =~ s/\x0d?\x0a$//;
 
-  return $line;
+  $line;
 }
 
 sub hmac_md5_sum { _hmac(\&_md5, @_) }
@@ -384,7 +384,6 @@ sub hmac_md5_sum { _hmac(\&_md5, @_) }
 sub hmac_sha1_sum { _hmac(\&_sha1, @_) }
 
 sub html_escape {
-
   my $escaped = '';
   for (1 .. length $_[0]) {
 
@@ -398,32 +397,37 @@ sub html_escape {
   $_[0] = $escaped;
 }
 
+# "Daddy, I'm scared. Too scared to even wet my pants.
+#  Just relax and it'll come, son."
 sub html_unescape {
-
-  # Unescape
   $_[0] =~ s/
     &
     (?:
-      \#(\d{1,7})             # Number
-      |
-      ([A-Za-z]{1,8})         # Named
+      \#
+      (
+        (?:
+          \d{1,7}             # Number
+          |
+          x[0-9A-Fa-f]{1,6}   # Hex
+        )
+      )
       |
-      \#x([0-9A-Fa-f]{1,6})   # Hex
+      ([A-Za-z]{1,8})         # Name
     )
     ;
-    /_unescape($1, $2, $3)/gex;
+  /_unescape($1, $2)/gex;
 }
 
 sub md5_bytes {
   my $data = shift;
   utf8::encode $data if utf8::is_utf8 $data;
-  return _md5($data);
+  _md5($data);
 }
 
 sub md5_sum {
   my $data = shift;
   utf8::encode $data if utf8::is_utf8 $data;
-  return Digest::MD5::md5_hex($data);
+  Digest::MD5::md5_hex($data);
 }
 
 sub punycode_decode {
@@ -449,14 +453,12 @@ sub punycode_decode {
       # Digit
       my $digit = ord substr $_[0], 0, 1, '';
       $digit = $digit < 0x40 ? $digit + (26 - 0x30) : ($digit & 0x1f) - 1;
-
       $i += $digit * $w;
       my $t = $k - $bias;
       $t =
           $t < PUNYCODE_TMIN ? PUNYCODE_TMIN
         : $t > PUNYCODE_TMAX ? PUNYCODE_TMAX
         :                      $t;
-
       last if $digit < $t;
 
       $w *= (PUNYCODE_BASE - $t);
@@ -464,7 +466,6 @@ sub punycode_decode {
 
     # Bias
     $bias = _adapt($i - $oldi, @output + 1, $oldi == 0);
-
     $n += $i / (@output + 1);
     $i = $i % (@output + 1);
 
@@ -472,12 +473,14 @@ sub punycode_decode {
     splice @output, $i, 0, chr($n);
     $i++;
   }
+
   $_[0] = join '', @output;
 }
 
 sub punycode_encode {
   use integer;
 
+  # Defaults
   my $output = $_[0];
   my $len    = length $_[0];
 
@@ -525,7 +528,6 @@ sub punycode_encode {
               $t < PUNYCODE_TMIN ? PUNYCODE_TMIN
             : $t > PUNYCODE_TMAX ? PUNYCODE_TMAX
             :                      $t;
-
           last if $q < $t;
 
           # Code point for digit "t"
@@ -540,7 +542,6 @@ sub punycode_encode {
 
         # Bias
         $bias = _adapt($delta, $h + 1, $h == $b);
-
         $delta = 0;
         $h++;
       }
@@ -549,6 +550,7 @@ sub punycode_encode {
     $delta++;
     $n++;
   }
+
   $_[0] = $output;
 }
 
@@ -569,7 +571,7 @@ sub quote {
 sub sha1_bytes {
   my $data = shift;
   utf8::encode $data if utf8::is_utf8 $data;
-  return _sha1($data);
+  _sha1($data);
 }
 
 sub sha1_sum {
@@ -579,7 +581,7 @@ Please install it manually or upgrade Perl to at least version 5.10.
 EOF
   my $data = shift;
   utf8::encode $data if utf8::is_utf8 $data;
-  return Digest::SHA::sha1_hex($data);
+  Digest::SHA::sha1_hex($data);
 }
 
 sub trim {
@@ -616,8 +618,6 @@ sub url_escape {
 
 sub url_unescape {
   return if index($_[0], '%') == -1;
-
-  # Unescape
   $_[0] =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/ge;
 }
 
@@ -638,11 +638,8 @@ sub _adapt {
   my ($delta, $numpoints, $firsttime) = @_;
 
   use integer;
-
-  # Delta
   $delta = $firsttime ? $delta / PUNYCODE_DAMP : $delta / 2;
   $delta += $delta / $numpoints;
-
   my $k = 0;
   while ($delta > ((PUNYCODE_BASE - PUNYCODE_TMIN) * PUNYCODE_TMAX) / 2) {
     $delta /= PUNYCODE_BASE - PUNYCODE_TMIN;
@@ -664,7 +661,7 @@ sub _hmac {
   # HMAC
   my $ipad = $secret ^ (chr(0x36) x 64);
   my $opad = $secret ^ (chr(0x5c) x 64);
-  return unpack 'H*', $_[0]->($opad . $_[0]->($ipad . $_[1]));
+  unpack 'H*', $_[0]->($opad . $_[0]->($ipad . $_[1]));
 }
 
 # Helper for md5_bytes
@@ -681,19 +678,11 @@ EOF
 
 # Helper for html_unescape
 sub _unescape {
-  my ($num, $entitie, $hex) = @_;
-
-  # Named to number
-  if (defined $entitie) { $num = $ENTITIES{$entitie} }
-
-  # Hex to number
-  elsif (defined $hex) { $num = hex $hex }
-
-  # Number
-  return pack 'U', $num if $num;
-
-  # Unknown entitie
-  return "&$entitie;";
+  if ($_[0]) {
+    return chr hex $_[0] if substr($_[0], 0, 1) eq 'x';
+    return chr $_[0];
+  }
+  exists $ENTITIES{$_[1]} ? chr $ENTITIES{$_[1]} : "&$_[1];";
 }
 
 1;
@@ -24,21 +24,21 @@ has ua => sub {
   my $ua = Mojo::UserAgent->new(app => $self, log => $self->log);
   weaken $ua->{app};
 
-  return $ua;
+  $ua;
 };
 
 # "Oh, so they have internet on computers now!"
 sub new {
   my $self = shift->SUPER::new(@_);
 
-  # Home
+  # Detect home directory
   $self->home->detect(ref $self);
 
   # Log directory
   $self->log->path($self->home->rel_file('log/mojo.log'))
     if -w $self->home->rel_file('log');
 
-  return $self;
+  $self;
 }
 
 # "D’oh."
@@ -105,15 +105,15 @@ The logging layer of your application, by default a L<Mojo::Log> object.
   my $cb = $app->on_transaction;
   $app   = $app->on_transaction(sub {...});
 
-The transaction builder callback, by default it builds a
-L<Mojo::Transaction::HTTP> object.
+Callback to be invoked when a new transaction is needed, by default it builds
+a L<Mojo::Transaction::HTTP> object.
 
 =head2 C<on_websocket>
 
   my $cb = $app->on_websocket;
   $app   = $app->on_websocket(sub {...});
 
-The websocket handshake callback, by default it builds a
+Callback to be invoked for WebSocket handshakes, by default it builds a
 L<Mojo::Transaction::WebSocket> object and handles the response for the
 handshake request.
 
@@ -23,7 +23,7 @@ sub run {
   GetOptions(nph => sub { $cgi->nph(1) });
   $cgi->run;
 
-  return $self;
+  $self;
 }
 
 1;
@@ -20,8 +20,6 @@ These options are available:
                           defaults to http://*:3000.
   --proxy                 Activate reverse proxy support, defaults to the
                           value of MOJO_REVERSE_PROXY.
-  --reload                Automatically reload application when the source
-                          code changes.
   --requests <number>     Set maximum number of requests per keep-alive
                           connection, defaults to 100.
   --user <name>           Set user name for process.
@@ -44,7 +42,6 @@ sub run {
     'keepalive=i' => sub { $daemon->keep_alive_timeout($_[1]) },
     'listen=s'    => \@listen,
     'proxy' => sub { $ENV{MOJO_REVERSE_PROXY} = 1 },
-    reload  => sub { $ENV{MOJO_RELOAD}        = 1 },
     'requests=i'  => sub { $daemon->max_requests($_[1]) },
     'user=s'      => sub { $daemon->user($_[1]) },
     'websocket=i' => sub { $daemon->websocket_timeout($_[1]) }
@@ -53,7 +50,7 @@ sub run {
   $daemon->listen(\@listen) if @listen;
   $daemon->run;
 
-  return $self;
+  $self;
 }
 
 1;
@@ -38,7 +38,7 @@ sub run {
   my $result = eval "package main; sub app { \$app }; $code";
   print "$result\n" if $verbose && defined $result;
   die $@ if $@;
-  return $result;
+  $result;
 }
 
 1;
@@ -15,7 +15,7 @@ sub run {
   my $self    = shift;
   my $fastcgi = Mojo::Server::FastCGI->new;
   $fastcgi->run;
-  return $self;
+  $self;
 }
 
 1;
@@ -19,9 +19,8 @@ Your application name has to be a well formed (camel case) Perl module name
 like "MyApp".
 EOF
 
-  my $name = $self->class_to_file($class);
-
   # Script
+  my $name = $self->class_to_file($class);
   $self->render_to_rel_file('mojo', "$name/script/$name", $class);
   $self->chmod_file("$name/script/$name", 0744);
 
@@ -25,7 +25,6 @@ __DATA__
 @@ liteapp
 %% my $class = shift;
 #!/usr/bin/env perl
-
 use Mojolicious::Lite;
 
 # Documentation browser under "/perldoc" (this plugin requires Perl 5.10)
@@ -17,7 +17,6 @@ sub run {
   my $class = $ENV{MOJO_APP} || 'MyApp';
   my $path  = $self->class_to_path($class);
   my $name  = $self->class_to_file($class);
-
   $self->render_to_rel_file('makefile', 'Makefile.PL', $class, $path, $name);
   $self->chmod_file('Makefile.PL', 0744);
 }
@@ -40,6 +40,7 @@ EOF
 sub run {
   my $self = shift;
 
+  # Options
   local @ARGV = @_ if @_;
   my $method = 'GET';
   my @headers;
@@ -61,11 +62,10 @@ sub run {
     $headers->{$1} = $2;
   }
 
-  # URL
+  # URL and selector
   my $url = shift @ARGV;
   die $self->usage unless $url;
   decode 'UTF-8', $url;
-
   my $selector = shift @ARGV;
 
   # Fresh user agent
@@ -151,7 +151,7 @@ sub run {
   # Select
   $self->_select($buffer, $charset, $selector) if $selector;
 
-  return $self;
+  $self;
 }
 
 sub _select {
@@ -48,7 +48,7 @@ sub run {
     $self->write_file($path, $content);
   }
 
-  return $self;
+  $self;
 }
 
 1;
@@ -20,7 +20,7 @@ sub run {
   $psgi->app;
 
   # Return app callback
-  return sub { $psgi->run(@_) };
+  sub { $psgi->run(@_) };
 }
 
 1;
@@ -14,17 +14,16 @@ EOF
 sub run {
   my $self = shift;
 
+  # Check if application has routes
   my $app = Mojo::Server->new->app;
   die "Application has no routes.\n" unless $app->can('routes');
 
-  # Walk
+  # Walk and draw
   my $routes = [];
   $self->_walk($_, 0, $routes) for @{$app->routes->children};
-
-  # Draw
   $self->_draw($routes);
 
-  return $self;
+  $self;
 }
 
 sub _draw {
@@ -56,7 +56,7 @@ sub run {
   # Run tests
   runtests(@tests);
 
-  return $self;
+  $self;
 }
 
 1;
@@ -69,7 +69,7 @@ OPTIONAL
 $message
 EOF
 
-  return $self;
+  $self;
 }
 
 1;
@@ -8,6 +8,7 @@ use Getopt::Long qw/GetOptions :config pass_through/;
 has hint => <<"EOF";
 
 These options are available for all commands:
+    --help          Get more information on a specific command.
     --home <path>   Path to your applications home directory, defaults to
                     the value of MOJO_HOME or auto detection.
     --mode <name>   Run mode of your application, defaults to the value of
@@ -17,9 +18,10 @@ See '$0 help COMMAND' for more information on a specific command.
 EOF
 has namespaces => sub { [qw/Mojolicious::Command Mojo::Command/] };
 
-# Command line options for MOJO_HOME and MOJO_MODE
+# Command line options for MOJO_HELP, MOJO_HOME and MOJO_MODE
 BEGIN {
   GetOptions(
+    'help|h' => sub { $ENV{MOJO_HELP} = 1 },
     'home=s' => sub { $ENV{MOJO_HOME} = $_[1] },
     'mode=s' => sub { $ENV{MOJO_MODE} = $_[1] }
   ) unless Mojo::Command->detect;
@@ -53,116 +55,116 @@ These commands are available by default.
 
 =head2 C<help>
 
-  mojo
-  mojo help
+  % mojo
+  % mojo help
 
 List available commands with short descriptions.
 
-  mojo help <command>
+  % mojo help <command>
 
 List available options for the command with short descriptions.
 
 =head2 C<cgi>
 
-  mojo cgi
-  script/myapp cgi
+  % mojo cgi
+  % script/myapp cgi
 
 Start application with CGI backend.
 
 =head2 C<daemon>
 
-  mojo daemon
-  script/myapp daemon
+  % mojo daemon
+  % script/myapp daemon
 
 Start application with standalone HTTP 1.1 server backend.
 
 =head2 C<eval>
 
-  mojo eval 'print app->home'
-  script/myapp eval 'print app->home'
+  % mojo eval 'print app->home'
+  % script/myapp eval 'print app->home'
 
 Run code against application.
 
 =head2 C<fastcgi>
 
-  mojo fastcgi
-  script/myapp fastcgi
+  % mojo fastcgi
+  % script/myapp fastcgi
 
 Start application with FastCGI backend.
 
 =head2 C<generate>
 
-  mojo generate
-  mojo generate help
+  % mojo generate
+  % mojo generate help
 
 List available generator commands with short descriptions.
 
-  mojo generate help <generator>
+  % mojo generate help <generator>
 
 List available options for generator command with short descriptions.
 
 =head2 C<generate app>
 
-  mojo generate app <AppName>
+  % mojo generate app <AppName>
 
 Generate application directory structure for a fully functional
 L<Mojolicious> application.
 
 =head2 C<generate gitignore>
 
-  mojo generate gitignore
+  % mojo generate gitignore
 
 Generate C<.gitignore> file.
 
 =head2 C<generate hypnotoad>
 
-  mojo generate hypnotoad
+  % mojo generate hypnotoad
 
 Generate C<hypnotoad.conf> file.
 
 =head2 C<generate lite_app>
 
-  mojo generate lite_app
+  % mojo generate lite_app
 
 Generate a fully functional L<Mojolicious::Lite> application.
 
 =head2 C<generate makefile>
 
-  mojo generate makefile
+  % mojo generate makefile
 
 Generate C<Makefile.PL> file for application.
 
 =head2 C<get>
 
-  mojo get http://mojolicio.us
-  script/myapp get /foo
+  % mojo get http://mojolicio.us
+  % script/myapp get /foo
 
 Perform GET request to remote host or local application.
 
 =head2 C<inflate>
 
-  myapp.pl inflate
+  % myapp.pl inflate
 
 Turn embedded files from the C<DATA> section into real files.
 
 =head2 C<routes>
 
-  myapp.pl routes
-  script/myapp routes
+  % myapp.pl routes
+  % script/myapp routes
 
 List application routes.
 
 =head2 C<test>
 
-  mojo test
-  script/myapp test
-  script/myapp test t/foo.t
+  % mojo test
+  % script/myapp test
+  % script/myapp test t/foo.t
 
 Runs application tests from the C<t> directory.
 
 =head2 C<version>
 
-  mojo version
+  % mojo version
 
 List version information for installed core and optional modules, very useful
 for debugging.
@@ -59,7 +59,7 @@ sub AUTOLOAD {
   # Call helper
   Carp::croak(qq/Can't locate object method "$method" via package "$package"/)
     unless my $helper = $self->app->renderer->helpers->{$method};
-  return $self->$helper(@_);
+  $self->$helper(@_);
 }
 
 sub DESTROY { }
@@ -97,7 +97,7 @@ sub cookie {
 
   # Request cookies
   my @cookies = $self->req->cookie($name);
-  return map { $_->value } @cookies;
+  map { $_->value } @cookies;
 }
 
 # "Something's wrong, she's not responding to my poking stick."
@@ -145,7 +145,7 @@ sub flash {
   my $values = exists $_[1] ? {@_} : $_[0];
   $session->{new_flash} = {%$flash, %$values};
 
-  return $self;
+  $self;
 }
 
 # "My parents may be evil, but at least they're stupid."
@@ -154,9 +154,8 @@ sub on_finish {
   $self->tx->on_finish(sub { shift and $self->$cb(@_) });
 }
 
-# "Stop being such a spineless jellyfish!
-#  You know full well I'm more closely related to the sea cucumber.
-#  Not where it counts."
+# "I like being a women.
+#  Now when I say something stupid, everyone laughs and buys me things."
 sub on_message {
   my $self = shift;
 
@@ -167,7 +166,7 @@ sub on_message {
   $tx->on_message(sub { shift and $self->$cb(@_) });
   $self->rendered(101);
 
-  return $self;
+  $self;
 }
 
 # "Just make a simple cake. And this time, if someone's going to jump out of
@@ -195,7 +194,7 @@ sub param {
   return $p->{$name} if !$RESERVED{$name} && exists $p->{$name};
 
   # Param value
-  return $self->req->param($name);
+  $self->req->param($name);
 }
 
 # "Is there an app for kissing my shiny metal ass?
@@ -209,7 +208,7 @@ sub redirect_to {
   $headers->content_length(0);
   $self->rendered(302);
 
-  return $self;
+  $self;
 }
 
 # "Mamma Mia! The cruel meatball of war has rolled onto our laps and ruined
@@ -224,10 +223,9 @@ sub render {
     return '';
   }
 
-  # Template as single argument
+  # Template may be first argument
   my $template;
   $template = shift if @_ % 2 && !ref $_[0];
-
   my $args = ref $_[0] ? $_[0] : {@_};
 
   # Template
@@ -256,11 +254,7 @@ sub render {
     $app->plugins->run_hook_reverse(before_render => $self, $args);
   }
   my ($output, $type) = $app->renderer->render($self, $args);
-
-  # Failed
   return unless defined $output;
-
-  # Partial
   return $output if $args->{partial};
 
   # Prepare response
@@ -270,10 +264,10 @@ sub render {
   $headers->content_type($type) unless $headers->content_type;
   $self->rendered($stash->{status});
 
-  # Success
-  return 1;
+  1;
 }
 
+# "She's built like a steakhouse, but she handles like a bistro!"
 sub render_content {
   my $self    = shift;
   my $name    = shift;
@@ -304,7 +298,7 @@ sub render_content {
   # Get
   $content = $c->{$name};
   $content = '' unless defined $content;
-  return Mojo::ByteStream->new("$content");
+  Mojo::ByteStream->new("$content");
 }
 
 sub render_data { shift->render(data => shift, @_) }
@@ -328,6 +322,7 @@ sub render_exception {
     $snapshot->{$key} = $value;
   }
 
+  # Mode specific template
   my $mode    = $self->app->mode;
   my $options = {
     template         => "exception.$mode",
@@ -338,8 +333,6 @@ sub render_exception {
     exception        => $e,
     'mojo.exception' => 1
   };
-
-  # Mode specific template
   unless ($self->render($options)) {
 
     # Template
@@ -347,8 +340,8 @@ sub render_exception {
     unless ($self->render($options)) {
 
       # Inline template
-      delete $options->{layout};
-      delete $options->{extends};
+      delete $stash->{layout};
+      delete $stash->{extends};
       delete $options->{template};
       $options->{inline} =
         $mode eq 'development' ? $DEVELOPMENT_EXCEPTION : $EXCEPTION;
@@ -374,7 +367,7 @@ sub render_json {
   my $json = shift;
   my $args = ref $_[0] ? $_[0] : {@_};
   $args->{json} = $json;
-  return $self->render($args);
+  $self->render($args);
 }
 
 sub render_later { shift->stash->{'mojo.rendered'} = 1 }
@@ -396,6 +389,7 @@ sub render_not_found {
     ? $self->url_for('/perldoc')
     : 'http://mojolicio.us/perldoc';
 
+  # Mode specific template
   my $mode    = $self->app->mode;
   my $options = {
     template         => "not_found.$mode",
@@ -404,8 +398,6 @@ sub render_not_found {
     guide            => $guide,
     'mojo.not_found' => 1
   };
-
-  # Mode specific template
   unless ($self->render($options)) {
 
     # Template
@@ -434,16 +426,21 @@ sub render_partial {
   $args->{template} = $template if defined $template;
   $args->{partial} = 1;
 
-  return Mojo::ByteStream->new($self->render($args));
+  Mojo::ByteStream->new($self->render($args));
 }
 
 sub render_static {
   my ($self, $file) = @_;
+
   my $app = $self->app;
-  $app->static->serve($self, $file)
-    and $app->log->debug(
-    qq/Static file "$file" not found, public directory missing?/);
+  unless ($app->static->serve($self, $file)) {
+    $app->log->debug(
+      qq/Static file "$file" not found, public directory missing?/);
+    return;
+  }
   $self->rendered;
+
+  1;
 }
 
 sub render_text { shift->render(text => shift, @_) }
@@ -466,15 +463,16 @@ sub rendered {
   unless ($stash->{'mojo.finished'}) {
     $res->code(200) unless $res->code;
     my $app = $self->app;
-    $app->sessions->store($self);
     $app->plugins->run_hook_reverse(after_dispatch => $self);
+    $app->sessions->store($self);
     $stash->{'mojo.finished'} = 1;
   }
   $self->tx->resume;
 
-  return $self;
+  $self;
 }
 
+# "A three month calendar? What is this, Mercury?"
 sub req { shift->tx->req }
 sub res { shift->tx->res }
 
@@ -487,7 +485,7 @@ sub send_message {
   $tx->send_message($message, sub { shift and $self->$cb(@_) if $cb });
   $self->rendered(101);
 
-  return $self;
+  $self;
 }
 
 # "Why am I sticky and naked? Did I miss something fun?"
@@ -511,7 +509,7 @@ sub session {
   my $values = exists $_[1] ? {@_} : $_[0];
   $stash->{'mojo.session'} = {%$session, %$values};
 
-  return $self;
+  $self;
 }
 
 sub signed_cookie {
@@ -555,7 +553,7 @@ sub signed_cookie {
     else { $self->app->log->debug(qq/Cookie "$name" not signed./) }
   }
 
-  return wantarray ? @results : $results[0];
+  wantarray ? @results : $results[0];
 }
 
 # "All this knowledge is giving me a raging brainer."
@@ -577,7 +575,7 @@ sub stash {
     $self->{stash}->{$key} = $values->{$key};
   }
 
-  return $self;
+  $self;
 }
 
 sub ua { shift->app->ua }
@@ -611,13 +609,29 @@ sub url_for {
 
   # Relative URL
   my $path = $url->path;
-  if ($target =~ /^\//) { $url->parse($target) }
+  if ($target =~ /^\//) {
+    if (my $e = $self->stash->{path}) {
+      my $real = $req->url->path->to_abs_string;
+      Mojo::Util::url_unescape($real);
+      my $backup = $real;
+      Mojo::Util::decode('UTF-8', $real);
+      $real = $backup unless defined $real;
+      $real =~ s/\/?$e$/$target/;
+      $target = $real;
+    }
+    $url->parse($target);
+  }
 
   # Route
   else {
     my ($p, $ws) = $match->path_for($target, @_);
     $path->parse($p) if $p;
 
+    # Fix trailing slash
+    $path->trailing_slash(1)
+      if (!$target || $target eq 'current')
+      && $req->url->path->trailing_slash;
+
     # Fix scheme for WebSockets
     $base->scheme(($base->scheme || '') eq 'https' ? 'wss' : 'ws') if $ws;
   }
@@ -627,7 +641,7 @@ sub url_for {
   unshift @{$path->parts}, @{$base_path->parts};
   $base_path->parts([]);
 
-  return $url;
+  $url;
 }
 
 # "I wax my rocket every day!"
@@ -641,7 +655,7 @@ sub write {
   $self->res->write($chunk, sub { shift and $self->$cb(@_) if $cb });
   $self->rendered;
 
-  return $self;
+  $self;
 }
 
 sub write_chunk {
@@ -654,7 +668,7 @@ sub write_chunk {
   $self->res->write_chunk($chunk, sub { shift and $self->$cb(@_) if $cb });
   $self->rendered;
 
-  return $self;
+  $self;
 }
 
 1;
@@ -741,21 +755,20 @@ Data storage persistent for the next request, stored in the session.
 
   $c->on_finish(sub {...});
 
-Callback signaling that the transaction has been finished.
+Callback to be invoked when the transaction has been finished.
 
   $c->on_finish(sub {
-    my $self = shift;
+    my $c = shift;
   });
 
 =head2 C<on_message>
 
   $c = $c->on_message(sub {...});
 
-Receive messages via WebSocket, only works if there is currently a WebSocket
-connection in progress.
+Callback to be invoked when new WebSocket messages arrive.
 
   $c->on_message(sub {
-    my ($self, $message) = @_;
+    my ($c, $message) = @_;
   });
 
 =head2 C<param>
@@ -868,8 +881,8 @@ Same as C<render> but returns the rendered result.
 
 =head2 C<render_static>
 
-  $c->render_static('images/logo.png');
-  $c->render_static('../lib/MyApp.pm');
+  my $success = $c->render_static('images/logo.png');
+  my $success = $c->render_static('../lib/MyApp.pm');
 
 Render a static file using L<Mojolicious::Static> relative to the
 C<public> directory of your application.
@@ -888,8 +901,8 @@ C<text/html;charset=UTF-8> by default.
 
 =head2 C<rendered>
 
-  $c->rendered;
-  $c->rendered(302);
+  $c = $c->rendered;
+  $c = $c->rendered(302);
 
 Finalize response and run C<after_dispatch> plugin hook.
 
@@ -978,6 +991,12 @@ A L<Mojo::UserAgent> prepared for the current environment.
 
 Generate a portable L<Mojo::URL> object with base for a route, path or URL.
 
+  # "/perldoc" if application is deployed under "/"
+  print $c->url_for('/perldoc');
+
+  # "/myapp/perldoc" if application is deployed under "/myapp"
+  print $c->url_for('/perldoc');
+
 =head2 C<write>
 
   $c->write;
@@ -990,12 +1009,19 @@ once all data has been written to the kernel send buffer or equivalent.
 
   # Keep connection alive (with Content-Length header)
   $c->res->headers->content_length(6);
-  $c->write('Hel', sub { shift->write('lo!') });
+  $c->write('Hel', sub {
+    my $c = shift;
+    $c->write('lo!')
+  });
 
   # Close connection when done (without Content-Length header)
-  $c->write('Hel');
-  $c->write('lo!');
-  $c->finish;
+  $c->write('Hel', sub {
+    my $c = shift;
+    $c->write('lo!', sub {
+      my $c = shift;
+      $c->finish;
+    });
+  });
 
 =head2 C<write_chunk>
 
@@ -1009,9 +1035,13 @@ which doesn't require a C<Content-Length> header, the optional drain callback
 will be invoked once all data has been written to the kernel send buffer or
 equivalent.
 
-  $c->write_chunk('He');
-  $c->write_chunk('ll');
-  $c->finish('o!');
+  $c->write_chunk('He', sub {
+    my $c = shift;
+    $c->write_chunk('ll', sub {
+      my $c = shift;
+      $c->finish('o!');
+    });
+  });
 
 You can call C<finish> at any time to end the stream.
 
@@ -1023,6 +1053,16 @@ You can call C<finish> at any time to end the stream.
   o!
   0
 
+=head1 HELPERS
+
+In addition to the attributes and methods above you can also call helpers on
+instances of L<Mojolicious::Controller>.
+This includes all helpers from L<Mojolicious::Plugin::DefaultHelpers> and
+L<Mojolicious::Plugin::TagHelpers>.
+
+  $c->layout('green');
+  $c->title('Welcome!');
+
 =head1 SEE ALSO
 
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
@@ -287,13 +287,6 @@ security reasons this is disabled by default.
 
   MOJO_PROXY=1
 
-=head2 C<MOJO_RELOAD>
-
-Enable L<Mojolicious> application reloading, changes to your application will
-be detected automatically so you don't have to restart the server manually.
-
-  MOJO_RELOAD=1
-
 =head2 C<MOJO_REVERSE_PROXY>
 
 Enable reverse proxy support for L<Mojolicious> application.
@@ -12,7 +12,7 @@ Cooking with L<Mojolicious>, recipes for every taste.
 Getting L<Mojolicious> and L<Mojolicious::Lite> applications running on
 different platforms.
 
-=head2 Builtin Server
+=head2 Built-in Server
 
 L<Mojolicious> contains a very portable HTTP 1.1 compliant web server.
 It is usually used during development but is solid and fast enough for small
@@ -47,7 +47,7 @@ of multiple cpu cores and copy-on-write.
   |- Mojo::Server::Daemon [3]
   `- Mojo::Server::Daemon [4]
 
-It is based on the normal builtin web server but optimized specifically for
+It is based on the normal built-in web server but optimized specifically for
 production environments out of the box.
 
   % hypnotoad script/myapp
@@ -68,7 +68,7 @@ connection, just by sending it a C<USR2> signal.
 
 =head2 Nginx
 
-One of the most popular setups these days is the builtin web server behind a
+One of the most popular setups these days is the built-in web server behind a
 Nginx reverse proxy.
 
   upstream myapp {
@@ -179,7 +179,20 @@ requests.
 
 =head2 Embedding
 
-You can also use the builtin web server to embed L<Mojolicious> applications
+From time to time you might want to reuse parts of L<Mojolicious>
+applications like configuration files, database connection or helpers for
+other scripts, with this little mock server you can just embed them.
+
+  use Mojo::Server;
+
+  # Load application with mock server
+  my $server = Mojo::Server->new;
+  my $app = $server->load_app('./myapp.pl');
+
+  # Access fully initialized application
+  print $app->static->root;
+
+You can also use the built-in web server to embed L<Mojolicious> applications
 into alien environments like foreign event loops.
 
   use Mojolicious::Lite;
@@ -337,14 +350,14 @@ Uploading a large file is even easier.
   # Upload file via POST and "multipart/form-data"
   my $ua = Mojo::UserAgent->new;
   $ua->post_form('mojolicio.us/upload',
-    {image => {file => '/Users/sri/hello.png'}});
+    {image => {file => '/home/sri/hello.png'}});
 
 And once again you don't have to worry about memory usage, all data will be
 streamed directly from the file.
 
   # Upload file via PUT
   my $ua     = Mojo::UserAgent->new;
-  my $asset  = Mojo::Asset::File->new(path => '/Users/sri/hello.png');
+  my $asset  = Mojo::Asset::File->new(path => '/home/sri/hello.png');
   my $tx     = $ua->build_tx(PUT => 'mojolicio.us/upload');
   $tx->req->content->asset($asset);
   $ua->start($tx);
@@ -410,56 +423,60 @@ You can just pick the parts that actually matter with the CSS3 selectors from
 L<Mojo::DOM>.
 
   % mojo get http://mojolicio.us 'head > title'
-  ...
 
 How about a list of all id attributes?
 
   % mojo get http://mojolicio.us '*' attr id
-  ...
 
 Or the text content of all header tags?
 
   % mojo get http://mojolicio.us 'h1, h2, h3' text
-  ...
 
 Maybe just the text of the third header?
 
   % mojo get http://mojolicio.us 'h1, h2, h3' 3 text
-  ...
 
 You can also extract all text from nested child elements.
 
   % mojo get http://mojolicio.us '#mojobar' all
-  ...
 
 The request can be customized as well.
 
   % mojo get --method post --content 'Hello!' http://mojolicio.us
-  ...
   % mojo get --header 'X-Bender: Bite my shiny metal ass!' http://google.com
-  ...
 
 You can follow redirects and view the headers for all messages.
 
   % mojo get --redirect --verbose http://reddit.com 'head > title'
-  ...
 
 This can be an invaluable tool for testing your applications.
 
   % ./myapp.pl get /welcome 'head > title'
-  ...
 
 =head1 HACKS
 
 Fun hacks you might not use very often but that might come in handy some day.
 
+=head2 Running Code Against Your Application
+
+Ever thought about running a quick oneliner against your L<Mojolicious>
+application to test something?
+Thanks to the C<eval> command you can do just that, the application instance
+itself can be accessed via C<app>.
+
+  % mojo generate lite_app
+  % ./myapp.pl eval 'print app->static->root, "\n"'
+
+The C<verbose> option will automatically print the return value to C<STDOUT>.
+
+  % ./myapp.pl eval -v 'app->static->root'
+
 =head2 Making Your Application Installable
 
 Ever thought about releasing your L<Mojolicious> application to CPAN?
 It's actually much easier than you might think.
 
   % mojo generate app
-  ...
   % cd my_mojolicious_app
   % mv public lib/MyMojoliciousApp/
   % mv templates lib/MyMojoliciousApp/
@@ -501,15 +518,10 @@ That's really everything, now you can package your application like any other
 CPAN module.
 
   % ./script/my_mojolicious_app generate makefile
-  ...
   % perl Makefile.PL
-  ...
   % make test
-  ...
   % make manifest
-  ...
   % make dist
-  ...
 
 =head2 Hello World
 
@@ -100,20 +100,14 @@ especially XS based modules due to its remarkable toolchain.
 With it you can even install modules straight from the source as you would do
 on a Unix based machine.
 
-=head2 Is it possible to run the builtin webserver on Windows?
+=head2 Is it possible to run the built-in webserver on Windows?
 
 It is!
-The builtin webserver is great way to run your L<Mojolicious> web application
-on any platform.
+The built-in webserver is great way to run your L<Mojolicious> web
+application on any platform.
 See L<Mojolicious::Guides::Cookbook> for more information about running and
 deploying L<Mojolicious> applications.
 
-Note that if you run your application with the C<--reload> option Windows
-will lock your files.
-A simple Windows editor like C<WordPad> will complain that the file has
-already been opened by a different proccess.
-More capable editors can handle this accordingly and force the change.
-
 =head2 Whats the easiest way to install L<Mojolicious> on UNIX?
 
 Quite possibly this oneliner.
@@ -166,10 +166,7 @@ organized CPAN distribution to maximize maintainability.
 Both application skeletons can be automatically generated.
 
   % mojo generate lite_app
-  ...
-
   % mojo generate app
-  ...
 
 =head2 Foundation
 
@@ -192,10 +189,10 @@ This will be the foundation for our login manager example application.
 
   app->start;
 
-The built-in web server makes working on your application a lot of fun thanks
-to automatic reloading.
+The built-in development web server makes working on your application a lot
+of fun thanks to automatic reloading.
 
-  % ./myapp.pl daemon --reload
+  % morbo myapp.pl
   Server available at http://127.0.0.1:3000.
 
 Just save your changes and they will be automatically in effect the next time
@@ -321,9 +318,7 @@ From now on you can always check your progress by running these unit tests
 against your application.
 
   % ./myapp.pl test
-  ...
   % ./myapp.pl test t/login.t
-  ...
 
 To make the tests less noisy and limit log output to just C<error> messages
 you can also add a line like this.
@@ -476,7 +471,6 @@ automatically turned into separate files in the C<templates> and C<public>
 directories.
 
   % ./myapp.pl inflate
-  ...
 
 Those directories always get priority, so inflating can also be a great way
 to allow your users to customize their applications.
@@ -151,16 +151,16 @@ application class.
 The renderer will always try to detect the right template but you can also
 use the C<template> stash value to render a specific one.
 
-  $self->render(template => 'foo');
+  $self->render(template => 'foo/bar');
 
 Choosing a specific C<format> and C<handler> is just as easy.
 
-  $self->render(template => 'foo', format => 'txt', handler => 'epl');
+  $self->render(template => 'foo/bar', format => 'txt', handler => 'epl');
 
 Because rendering a specific template is the most common task it also has a
 shortcut.
 
-  $self->render('foo');
+  $self->render('foo/bar');
 
 All values passed to the C<render> call are only temporarily assigned to the
 stash and get reset again once rendering is finished.
@@ -546,7 +546,6 @@ C<DATA> section into actual files in the C<templates> and C<public>
 directories.
 
   % ./myapp.pl inflate
-  ...
 
 =head2 Customizing The Template Syntax
 
@@ -77,6 +77,10 @@ best of both worlds with a little bit of experience.
 
   /user/show/:id -> qr/(?-xism:^\/user\/show/([^\/\.]+))/
 
+A trailing slash is always optional.
+
+  /user/show/23/ -> /user/:action/:id -> {action => 'show', id => 23}
+
 =head2 Reversibility
 
 One more huge advantage routes have over regular expressions is that they are
@@ -279,7 +283,7 @@ The C<controller> is always appended to the C<namespace> if available.
   # /bye -> MyApp::Controller::Foo->bye
   $r->route('/bye')->to('foo#bye', namespace => 'MyApp::Controller');
 
-You can also change the default namespace for all routes.
+You can also change the default namespace for all routes in the application.
 
   $r->namespace('MyApp::Controller');
 
@@ -296,6 +300,36 @@ callback instead.
 This technique is the foundation of L<Mojolicious::Lite>, you can learn more
 about it from the included tutorial.
 
+=head2 More Restrictive Placeholders
+
+A very easy way to make placeholders more restrictive are alternatives, you
+just make a list of possible values.
+
+  # /bender -> {controller => 'foo', action => 'bar', name => 'bender'}
+  # /leela  -> {controller => 'foo', action => 'bar', name => 'leela'}
+  # /fry    -> undef
+  $r->route('/:name', name => [qw/bender leela/])
+    ->to(controller => 'foo', action => 'bar');
+
+You can also adjust the regular expressions behind placeholders to better
+suit your needs.
+Just make sure not to use C<^> and C<$> or capturing groups C<(...)>, because
+placeholders become part of a larger regular expression internally,
+C<(?:...)> is fine though.
+
+  # /23   -> {controller => 'foo', action => 'bar', number => 23}
+  # /test -> undef
+  $r->route('/:number', number => qr/\d+/)
+    ->to(controller => 'foo', action => 'bar');
+
+  # /23   -> undef
+  # /test -> {controller => 'foo', action => 'bar', name => 'test'}
+  $r->route('/:name', name => qr/[a-zA-Z]+/)
+    ->to(controller => 'foo', action => 'bar');
+
+This way you get easily readable routes and the raw power of regular
+expressions.
+
 =head2 Formats
 
 File extensions like C<.html> and C<.txt> at the end of a route are
@@ -308,6 +342,30 @@ automatically detected and stored in the stash value C<format>.
 
 This for example allows multiple templates for different formats to share the
 same code.
+You can also mention a format in the route pattern to only match one, just
+make sure the more specific routes go first.
+
+  # /foo.txt -> {controller => 'foo', action => 'text', format => 'txt'}
+  $r->route('/foo.txt')->to(controller => 'foo', action => 'text');
+
+  # /foo      -> {controller => 'foo', action => 'hyper'}
+  # /foo.html -> {controller => 'foo', action => 'hyper', format => 'html'}
+  $r->route('/foo')->to(controller => 'foo', action => 'hyper');
+
+Restrictive placeholders can also be used for format detection.
+
+  # /foo.rss -> {controller => 'foo', action => 'feed', format => 'rss'}
+  # /foo.xml -> {controller => 'foo', action => 'feed', format => 'xml'}
+  # /foo.txt -> undef
+  $r->route('/foo', format => [qw/rss xml/])
+    ->to(controller => 'foo', action => 'feed');
+
+Or you can just disable format detection.
+
+  # /foo      -> {controller => 'foo', action => 'bar'}
+  # /foo.html -> undef
+  $r->route('/foo', format => undef)
+    ->to(controller => 'foo', action => 'bar');
 
 =head2 Placeholders And Destinations
 
@@ -485,27 +543,6 @@ will be broken, this makes bridges a very powerful tool for authentication.
   });
   $foo->route('/bar')->to(controller => 'foo', action => 'bar');
 
-=head2 More Restrictive Placeholders
-
-You can adjust the regular expressions behind placeholders to better suit
-your needs.
-Just make sure not to use C<^> and C<$> or capturing groups C<(...)>, because
-placeholders become part of a larger regular expression internally,
-C<(?:...)> is fine though.
-
-  # /23   -> {controller => 'foo', action => 'bar', number => 23}
-  # /test -> undef
-  $r->route('/:number', number => qr/\d+/)
-    ->to(controller => 'foo', action => 'bar');
-
-  # /23   -> undef
-  # /test -> {controller => 'foo', action => 'bar', name => 'test'}
-  $r->route('/:name', name => qr/[a-zA-Z]+/)
-    ->to(controller => 'foo', action => 'bar');
-
-This way you get easily readable routes and the raw power of regular
-expressions.
-
 =head2 Shortcuts
 
 You can also add your own shortcuts to make route generation more expressive.
@@ -657,11 +694,18 @@ containing a C<handler> method accepting L<Mojolicious::Controller> objects.
 
   1;
 
-Because the remaining path always gets stored in the C<path> stash value, you
-could also just use it directly instead of C<detour>.
+You can also just use L<Mojolicious::Plugin::Mount> to mount whole
+self-contained applications under a prefix.
 
-  # /foo/*
-  $r->route('/foo/*path')->to('bar#', name => 'Mojo');
+  use Mojolicious::Lite;
+
+  # Whole application mounted under "/prefix"
+  plugin mount => {'/prefix' => '/home/sri/myapp.pl'};
+
+  # Normal route
+  get '/' => sub { shift->render_text('Hello World!') };
+
+  app->start;
 
 =head2 Application Plugins
 
@@ -12,8 +12,17 @@ updates.
 
 =head1 TUTORIAL
 
+=over 2
+
+=item L<Mojolicious::Lite>
+
 A really fast and fun way to get started developing web applications with
-Mojolicious is the tutorial in L<Mojolicious::Lite>, you should take a look.
+Mojolicious is the L<Mojolicious::Lite> tutorial.
+Almost everything you learn there can also be applied to normal
+L<Mojolicious> applications and is considered a prerequisite for the guides.
+You should definitely take a look!
+
+=back
 
 =head1 GUIDES
 
@@ -73,8 +82,8 @@ Minimalistic JSON implementation that just works.
 
 =item L<Mojo::Server::Daemon>
 
-Highly portable async io HTTP 1.1 and WebSocket server, perfect for
-development and testing.
+Highly portable async io HTTP 1.1 and WebSocket server with self-restart
+support through L<Mojo::Server::Morbo>, perfect for development and testing.
 
 =item L<Mojo::Server::Hypnotoad>
 
@@ -95,7 +104,7 @@ Countless portable and very convenient bytestream manipulation methods.
 
 =item L<Mojolicious::Commands>
 
-Pluggable command line system and the backbone of the C<mojo> script.
+Pluggable command line system and the backbone of the L<mojo> script.
 
 =item L<Test::Mojo>
 
@@ -45,10 +45,8 @@ sub import {
   $app->static->default_static_class($caller);
   $app->renderer->default_template_class($caller);
 
-  # Root
-  my $root = $routes;
-
   # Export
+  my $root = $routes;
   *{"${caller}::new"} = *{"${caller}::app"} = sub {$app};
   *{"${caller}::any"}    = sub { $routes->any(@_) };
   *{"${caller}::del"}    = sub { $routes->del(@_) };
@@ -103,16 +101,18 @@ applications.
 
 =head2 Hello World!
 
-A minimal Hello World application looks like this, L<strict> and L<warnings>
-are automatically enabled and a few functions imported when you use
-L<Mojolicious::Lite>, turning your script into a full featured web
+A simple Hello World application can look like this, L<strict> and
+L<warnings> are automatically enabled and a few functions imported when you
+use L<Mojolicious::Lite>, turning your script into a full featured web
 application.
 
   #!/usr/bin/env perl
-
   use Mojolicious::Lite;
 
-  get '/' => sub { shift->render(text => 'Hello World!') };
+  get '/' => sub {
+    my $self = shift;
+    $self->render(text => 'Hello World!');
+  };
 
   app->start;
 
@@ -153,10 +153,11 @@ customized to override normal C<@ARGV> use.
 
 =head2 Reloading
 
-Your application will automatically reload itself if you set the C<--reload>
-option, so you don't have to restart the server after every change.
+Your application will automatically reload itself if you start it with the
+C<morbo> development web server, so you don't have to restart the server
+after every change.
 
-  % ./myapp.pl daemon --reload
+  % morbo myapp.pl
   Server available at http://127.0.0.1:3000.
 
 =head2 Routes
@@ -276,8 +277,8 @@ delimited by the C<begin> and C<end> keywords.
   <!doctype html><html>
     <head><title>Sebastians Frameworks!</title></head>
     <body>
-      <%== $link->('http://mojolicio.us', 'Mojolicious') %>
-      <%== $link->('http://catalystframework.org', 'Catalyst') %>
+      <%= $link->('http://mojolicio.us', 'Mojolicious') %>
+      <%= $link->('http://catalystframework.org', 'Catalyst') %>
     </body>
   </html>
 
@@ -400,32 +401,6 @@ Routes can be restricted to specific request methods.
     $self->render(text => "You called /baz with $method");
   };
 
-=head2 Route Constraints
-
-All placeholders get compiled to a regex internally, with regex constraints
-this process can be easily customized.
-
-  # /1
-  # /123
-  any '/:foo' => [foo => qr/\d+/] => sub {
-    my $self = shift;
-    my $foo  = $self->param('foo');
-    $self->render(text => "Our :foo placeholder matched $foo");
-  };
-
-  # /test
-  # /test.123
-  # /test/1.2.3
-  any '/:bar' => [bar => qr/.*/] => sub {
-    my $self = shift;
-    my $bar  = $self->param('bar');
-    $self->render(text => "Our :bar placeholder matched $bar");
-  };
-
-Just make sure not to use C<^> and C<$> or capturing groups C<(...)>, because
-placeholders become part of a larger regular expression internally,
-C<(?:...)> is fine though.
-
 =head2 Optional Placeholders
 
 Routes allow default values to make placeholders optional.
@@ -442,71 +417,66 @@ Routes allow default values to make placeholders optional.
   @@ groovy.txt.ep
   My name is <%= $name %>.
 
-=head2 A Little Bit Of Everything
+=head2 Restrictive Placeholders
 
-All those features can be easily used together.
+The easiest way to make placeholders more restrictive are alternatives, you
+just make a list of possible values.
 
-  # /everything?name=Sebastian
-  # /everything/123?name=Sebastian
-  get '/everything/:stuff' => [stuff => qr/\d+/] => {stuff => 23} => sub {
-    shift->render('welcome');
+  # /test
+  # /123
+  any '/:foo' => [foo => [qw/test 123/]] => sub {
+    my $self = shift;
+    my $foo  = $self->param('foo');
+    $self->render(text => "Our :foo placeholder matched $foo");
   };
 
-  __DATA__
-
-  @@ welcome.html.ep
-  Stuff is <%= $stuff %>.
-  Query param name is <%= param 'name' %>.
+All placeholders get compiled to a regex internally, this process can also be
+easily customized.
 
-Here's a fully functional example for a html form handling application using
-multiple features at once.
+  # /1
+  # /123
+  any '/:bar' => [bar => qr/\d+/] => sub {
+    my $self = shift;
+    my $bar  = $self->param('bar');
+    $self->render(text => "Our :bar placeholder matched $bar");
+  };
 
-  #!/usr/bin/env perl
+Just make sure not to use C<^> and C<$> or capturing groups C<(...)>, because
+placeholders become part of a larger regular expression internally,
+C<(?:...)> is fine though.
 
-  use Mojolicious::Lite;
+=head2 Formats
 
-  get '/' => 'index';
+Formats can be automatically detected by looking at file extensions.
 
-  post '/test' => sub {
+  # /detection.html
+  # /detection.txt
+  get '/detection' => sub {
     my $self = shift;
+    $self->render('detected');
+  };
 
-    my $groovy = $self->param('groovy') || 'Austin Powers';
-    $groovy =~ s/[^\w\s]+//g;
-
-    $self->render(
-      template => 'welcome',
-      title    => 'Welcome!',
-      layout   => 'funky',
-      groovy   => $groovy
-    );
-  } => 'test';
-
-  app->start;
   __DATA__
 
-  @@ index.html.ep
-  % title 'Groovy!';
-  % layout 'funky';
-  Who is groovy?
-  <%= form_for test => (method => 'post') => begin %>
-    <%= text_field 'groovy' %>
-    <%= submit_button 'Woosh!' %>
-  <% end %>
+  @@ detected.html.ep
+  <!doctype html><html>
+    <head><title>Detected!</title></head>
+    <body>HTML was detected.</body>
+  </html>
 
-  @@ welcome.html.ep
-  <%= $groovy %> is groovy!
-  <%= include 'menu' %>
+  @@ detected.txt.ep
+  TXT was detected.
 
-  @@ menu.html.ep
-  <%= link_to index => begin %>
-    Try again
-  <% end %>
+Restrictive placeholders can also be used for format detection.
 
-  @@ layouts/funky.html.ep
-  <!doctype html><html>
-    <head><title><%= title %></title></head>
-    <body><%= content %></body>
-  </html>
+  # /hello.json
+  # /hello.txt
+  get '/hello' => [format => [qw/json txt/]] => sub {
+    my $self = shift;
+    return $self->render_json({hello => 'world!'})
+      if $self->stash('format') eq 'json';
+    $self->render_text('hello world!');
+  };
 
 =head2 Under
 
@@ -559,7 +529,9 @@ Prefixing multiple routes is another good use for C<under>.
 
 =head2 Conditions
 
-Conditions such as C<agent> allow even more powerful route constructs.
+Conditions such as C<agent> and C<host> from
+L<Mojolicious::Plugin::HeaderCondition> allow even more powerful route
+constructs.
 
   # /foo
   get '/foo' => (agent => qr/Firefox/) => sub {
@@ -571,27 +543,16 @@ Conditions such as C<agent> allow even more powerful route constructs.
     shift->render(text => 'Dude, you really need to upgrade to Firefox!');
   };
 
-=head2 Formats
-
-Formats can be automatically detected by looking at file extensions.
-
-  # /detection.html
-  # /detection.txt
-  get '/detection' => sub {
-    my $self = shift;
-    $self->render('detected');
+  # /bar
+  get '/bar' => (host => 'mojolicio.us') => sub {
+    shift->render(text => 'Hello Mojolicious!');
   };
 
-  __DATA__
+However you might want to disable automatic route caching in case there are
+routes responding to the same path without conditions attached, since those
+would otherwise get precedence once cached.
 
-  @@ detected.html.ep
-  <!doctype html><html>
-    <head><title>Detected!</title></head>
-    <body>HTML was detected.</body>
-  </html>
-
-  @@ detected.txt.ep
-  TXT was detected.
+  app->routes->cache(0);
 
 =head2 Sessions
 
@@ -1,66 +0,0 @@
-package Mojolicious::Plugin::AgentCondition;
-use Mojo::Base 'Mojolicious::Plugin';
-
-# "Wow, there's a million aliens! I've never seen something so mind-blowing!
-#  Ooh, a reception table with muffins!"
-sub register {
-  my ($self, $app) = @_;
-
-  # Agent
-  $app->routes->add_condition(
-    agent => sub {
-      my ($r, $c, $captures, $pattern) = @_;
-
-      # Pattern
-      return unless $pattern && ref $pattern eq 'Regexp';
-
-      # Match
-      my $agent = $c->req->headers->user_agent;
-      return 1 if $agent && $agent =~ $pattern;
-
-      # Nothing
-      return;
-    }
-  );
-}
-
-1;
-__END__
-
-=head1 NAME
-
-Mojolicious::Plugin::AgentCondition - Agent Condition Plugin
-
-=head1 SYNOPSIS
-
-  # Mojolicious
-  $self->plugin('agent_condition');
-  $self->routes->route('/:controller/:action')->over(agent => qr/Firefox/);
-
-  # Mojolicious::Lite
-  plugin 'agent_condition';
-  get '/' => (agent => qr/Firefox/) => sub {...};
-
-=head1 DESCRIPTION
-
-L<Mojolicious::Plugin::AgentCondition> is a routes condition for user agent
-based routes.
-This is a core plugin, that means it is always enabled and its code a good
-example for learning to build new plugins.
-
-=head1 METHODS
-
-L<Mojolicious::Plugin::AgentCondition> inherits all methods from
-L<Mojolicious::Plugin> and implements the following new ones.
-
-=head2 C<register>
-
-  $plugin->register;
-
-Register condition in L<Mojolicious> application.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
-
-=cut
@@ -0,0 +1,66 @@
+package Mojolicious::Plugin::CallbackCondition;
+use Mojo::Base 'Mojolicious::Plugin';
+
+# "Stop being such a spineless jellyfish!
+#  You know full well I'm more closely related to the sea cucumber.
+#  Not where it counts."
+sub register {
+  my ($self, $app) = @_;
+
+  # "cb" condition
+  $app->routes->add_condition(
+    cb => sub {
+      my ($r, $c, $captures, $cb) = @_;
+      return unless $cb && ref $cb eq 'CODE';
+      $r->$cb($c, $captures);
+    }
+  );
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Plugin::CallbackCondition - Callback Condition Plugin
+
+=head1 SYNOPSIS
+
+  # Mojolicious
+  $self->plugin('callback_condition');
+  $self->routes->route('/:controller/:action')->over(cb => sub {
+    my ($r, $c, $captures) = @_;
+    ...
+  });
+
+  # Mojolicious::Lite
+  plugin 'callback_condition';
+  get '/' => (cb => sub {
+    my ($r, $c, $captures) = @_;
+    ...
+  }) => sub {...};
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Plugin::CallbackCondition> is a routes condition for
+callbacks.
+This is a core plugin, that means it is always enabled and its code a good
+example for learning to build new plugins.
+Note that this module is EXPERIMENTAL and might change without warning!
+
+=head1 METHODS
+
+L<Mojolicious::Plugin::CallbackCondition> inherits all methods from
+L<Mojolicious::Plugin> and implements the following new ones.
+
+=head2 C<register>
+
+  $plugin->register;
+
+Register condition in L<Mojolicious> application.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
+
+=cut
@@ -7,11 +7,8 @@ use Mojo::Base 'Mojolicious::Plugin';
 sub register {
   my ($self, $app, $conf) = @_;
 
-  # Config
-  $conf ||= {};
-
-
   # Got a charset
+  $conf ||= {};
   if (my $charset = $conf->{charset}) {
 
     # Add charset to text/html content type
@@ -17,7 +17,7 @@ sub load {
   my $content = do { local $/; <$handle> };
 
   # Process
-  return $self->parse($content, $file, $conf, $app);
+  $self->parse($content, $file, $conf, $app);
 }
 
 sub parse {
@@ -30,7 +30,7 @@ sub parse {
   die qq/Config file "$file" did not return a hashref.\n/
     unless ref $config && ref $config eq 'HASH';
 
-  return $config;
+  $config;
 }
 
 sub register {
@@ -88,14 +88,14 @@ sub register {
     config => sub {
       my $self = shift;
       return $config unless @_;
-      return $config->{$_[0]};
+      $config->{$_[0]};
     }
   );
 
-  # Default
+  # Add default stash value
   $app->defaults(($conf->{stash_key} || 'config') => $config);
 
-  return $config;
+  $config;
 }
 
 1;
@@ -8,8 +8,23 @@ require Data::Dumper;
 sub register {
   my ($self, $app) = @_;
 
-  # Add "app" helper
-  $app->helper(app => sub { shift->app });
+  # Controller alias helpers
+  for my $name (qw/app flash param stash session url_for/) {
+    $app->helper($name => sub { shift->$name(@_) });
+  }
+
+  # Stash key shortcuts
+  for my $name (qw/extends layout title/) {
+    $app->helper(
+      $name => sub {
+        my $self  = shift;
+        my $stash = $self->stash;
+        $stash->{$name} = shift if @_;
+        $self->stash(@_) if @_;
+        $stash->{$name};
+      }
+    );
+  }
 
   # Add "content" helper
   $app->helper(content => sub { shift->render_content(@_) });
@@ -31,20 +46,6 @@ sub register {
     }
   );
 
-  # Add "extends" helper
-  $app->helper(
-    extends => sub {
-      my $self  = shift;
-      my $stash = $self->stash;
-      $stash->{extends} = shift if @_;
-      $self->stash(@_) if @_;
-      return $stash->{extends};
-    }
-  );
-
-  # Add "flash" helper
-  $app->helper(flash => sub { shift->flash(@_) });
-
   # Add "include" helper
   $app->helper(
     include => sub {
@@ -66,18 +67,7 @@ sub register {
       $i++;
       goto START unless $i >= @keys;
 
-      return $self->render_partial(layout => $layout, extend => $extends);
-    }
-  );
-
-  # Add "layout" helper
-  $app->helper(
-    layout => sub {
-      my $self  = shift;
-      my $stash = $self->stash;
-      $stash->{layout} = shift if @_;
-      $self->stash(@_) if @_;
-      return $stash->{layout};
+      $self->render_partial(layout => $layout, extend => $extends);
     }
   );
 
@@ -89,7 +79,6 @@ sub register {
       my $cb = pop;
       return '' unless ref $cb && ref $cb eq 'CODE';
       my $name = shift;
-
       my $args;
       if (ref $name && ref $name eq 'HASH') {
         $args = $name;
@@ -115,31 +104,6 @@ sub register {
       $memorize->{$name}->{content} = $cb->();
     }
   );
-
-  # Add "param" helper
-  $app->helper(
-    param => sub { wantarray ? (shift->param(@_)) : scalar shift->param(@_); }
-  );
-
-  # Add "session" helper
-  $app->helper(session => sub { shift->session(@_) });
-
-  # Add "stash" helper
-  $app->helper(stash => sub { shift->stash(@_) });
-
-  # Add "title" helper
-  $app->helper(
-    title => sub {
-      my $self  = shift;
-      my $stash = $self->stash;
-      $stash->{title} = shift if @_;
-      $self->stash(@_) if @_;
-      return $stash->{title};
-    }
-  );
-
-  # Add "url_for" helper
-  $app->helper(url_for => sub { shift->url_for(@_) });
 }
 
 1;
@@ -166,6 +130,12 @@ example for learning to build new plugins.
 
 =head1 HELPERS
 
+=head2 C<app>
+
+  <%= app->secret %>
+
+Alias for the C<app> method in L<Mojolicious::Controller>.
+
 =head2 C<content>
 
   <%= content %>
@@ -205,7 +175,7 @@ Extend a template.
 
   <%= flash 'foo' %>
 
-Access flash values.
+Alias for the C<flash> method in L<Mojolicious::Controller>.
 
 =head2 C<include>
 
@@ -242,20 +212,20 @@ Memorize block result in memory and prevent future execution.
 
   <%= param 'foo' %>
 
-Access GET/POST parameters and route captures.
+Alias for the C<param> method in L<Mojolicious::Controller>.
 
 =head2 C<session>
 
   <%= session 'foo' %>
 
-Access session values.
+Alias for the C<session> method in L<Mojolicious::Controller>.
 
 =head2 C<stash>
 
   <%= stash 'foo' %>
   <% stash foo => 'bar'; %>
 
-Access stash values.
+Alias for the C<stash> method in L<Mojolicious::Controller>.
 
 =head2 C<title>
 
@@ -272,7 +242,13 @@ Page title.
   <%= url_for '/perldoc' %>
   <%= url_for 'http://mojolicio.us/perldoc' %>
 
-Generate a portable L<Mojo::URL> object with base for a route, path or URL.
+Alias for the C<url_for> method in L<Mojolicious::Controller>.
+
+  %# "/perldoc" if application is deployed under "/"
+  %= url_for '/perldoc'
+
+  %# "/myapp/perldoc" if application is deployed under "/myapp"
+  %= url_for '/perldoc'
 
 =head1 METHODS
 
@@ -1,6 +1,7 @@
 package Mojolicious::Plugin::EpRenderer;
 use Mojo::Base 'Mojolicious::Plugin';
 
+use Mojo::Loader;
 use Mojo::Template;
 use Mojo::Util 'md5_sum';
 
@@ -15,6 +16,11 @@ sub register {
   my $name     = $conf->{name}     || 'ep';
   my $template = $conf->{template} || {};
 
+  # Custom sandbox
+  $template->{namespace} =
+    'Mojo::Template::SandBox::' . md5_sum(($ENV{MOJO_EXE} || ref $app) . $$)
+    unless defined $template->{namespace};
+
   # Auto escape by default to prevent XSS attacks
   $template->{auto_escape} = 1 unless defined $template->{auto_escape};
 
@@ -29,8 +35,6 @@ sub register {
       my $list = join ', ', sort keys %{$c->stash};
       my $key = $options->{cache} = md5_sum "$path($list)";
 
-      $c->stash->{layout} ||= undef;
-
       # Cache
       my $cache = $r->cache;
       unless ($cache->get($key)) {
@@ -50,7 +54,7 @@ sub register {
         for my $name (sort keys %{$r->helpers}) {
           next unless $name =~ /^\w+$/;
           $prepend .= "sub $name; *$name = sub { ";
-          $prepend .= "return \$_H->{'$name'}->(\$self, \@_) };";
+          $prepend .= "\$_H->{'$name'}->(\$self, \@_) };";
         }
 
         # Be less relaxed for everything else
@@ -71,7 +75,7 @@ sub register {
       }
 
       # Render with epl
-      return $r->handlers->{epl}->($r, $c, $output, $options);
+      $r->handlers->{epl}->($r, $c, $output, $options);
     }
   );
 
@@ -13,10 +13,10 @@ sub register {
   $app->renderer->add_handler(
     epl => sub {
       my ($r, $c, $output, $options) = @_;
-      my $inline = $options->{inline};
 
       # Template
-      my $path = $r->template_path($options);
+      my $inline = $options->{inline};
+      my $path   = $r->template_path($options);
       $path = md5_sum $inline if defined $inline;
       return unless defined $path;
 
@@ -25,9 +25,8 @@ sub register {
       my $key   = delete $options->{cache} || $path;
       my $mt    = $cache->get($key);
 
-      $mt ||= Mojo::Template->new;
-
       # Cached
+      $mt ||= Mojo::Template->new;
       if ($mt->compiled) { $$output = $mt->interpret($c) }
 
       # Not cached
@@ -79,7 +78,7 @@ sub register {
       }
 
       # Success or exception
-      return ref $$output ? 0 : 1;
+      ref $$output ? 0 : 1;
     }
   );
 }
@@ -7,35 +7,32 @@ use Mojo::Base 'Mojolicious::Plugin';
 sub register {
   my ($self, $app) = @_;
 
-  # Header
+  # "headers" condition
+  $app->routes->add_condition(headers => \&_headers);
+
+  # "agent" condition
+  $app->routes->add_condition(
+    agent => sub { _headers(@_[0 .. 2], {'User-Agent' => $_[3]}) });
+
+  # "host" condition
   $app->routes->add_condition(
-    headers => sub {
-      my ($r, $c, $captures, $patterns) = @_;
-
-      # Patterns
-      return unless $patterns && ref $patterns eq 'HASH';
-
-      # Match
-      my $passed;
-      while (my ($k, $v) = each(%$patterns)) {
-        my $header = $c->req->headers->header($k);
-        if ($header && $v && ref $v eq 'Regexp' && $header =~ $v) {
-          $passed = 1;
-          next;
-        }
-        elsif ($header && defined $v && $v eq $header) {
-          $passed = 1;
-          next;
-        }
-        $passed = undef;
-      }
-
-      # Success
-      return 1 if $passed;
-
-      return;
-    }
-  );
+    host => sub { _headers(@_[0 .. 2], {'Host' => $_[3]}) });
+}
+
+# "Wow, there's a million aliens! I've never seen something so mind-blowing!
+#  Ooh, a reception table with muffins!"
+sub _headers {
+  my ($r, $c, $captures, $patterns) = @_;
+  return unless $patterns && ref $patterns eq 'HASH' && keys %$patterns;
+
+  # All headers need to match
+  while (my ($k, $v) = each %$patterns) {
+    my $header = $c->req->headers->header($k);
+    if ($header && $v && ref $v eq 'Regexp' && $header =~ $v) {next}
+    elsif ($header && defined $v && $v eq $header) {next}
+    else                                           {return}
+  }
+  return 1;
 }
 
 1;
@@ -49,22 +46,31 @@ Mojolicious::Plugin::HeaderCondition - Header Condition Plugin
 
   # Mojolicious
   $self->plugin('header_condition');
-
-  # Must match all of these headers
-  $self->routes->route('/:controller/:action')->over(headers => {
-    X-Secret-Header => 'Foo',
-    Referer => qr/^https?:\/\/example\.com\//
-  })->to('foo#bar');
+  $self->routes->route('/:controller/:action')
+    ->over(headers => {Referer => qr/example\.com/});
 
   # Mojolicious::Lite
   plugin 'header_condition';
-  get '/' => (headers => {'Referer' => qr/^https?:\/\/example\.com\//})
-    => sub {...};
+  get '/' => (headers => {Referer => qr/example\.com/}) => sub {...};
+
+  # All headers need to match
+  $self->routes->route('/:controller/:action')->over(headers => {
+    'X-Secret-Header' => 'Foo',
+    Referer => qr/example\.com/
+  });
+
+  # The "agent" condition is a shortcut for the "User-Agent" header
+  get '/' => (agent => qr/Firefox/) => sub {...};
+
+  # The "host" condition is a shortcut for the "Host" header
+  get '/' => (host => qr/mojolicio\.us/) => sub {...};
 
 =head1 DESCRIPTION
 
 L<Mojolicious::Plugin::HeaderCondition> is a routes condition for header
 based routes.
+This is a core plugin, that means it is always enabled and its code a good
+example for learning to build new plugins.
 
 =head1 METHODS
 
@@ -8,12 +8,11 @@ use I18N::LangTags::Detect;
 #  No, the cat shelter’s onto me."
 sub register {
   my ($self, $app, $conf) = @_;
-
   $conf ||= {};
-  my $namespace = $conf->{namespace} || ((ref $app) . "::I18N");
-  my $default   = $conf->{default}   || 'en';
 
   # Initialize
+  my $namespace = $conf->{namespace} || ((ref $app) . "::I18N");
+  my $default   = $conf->{default}   || 'en';
   eval "package $namespace; use base 'Locale::Maketext'; 1;";
   eval "require ${namespace}::${default};";
   unless (eval "\%${namespace}::${default}::Lexicon") {
@@ -50,14 +49,12 @@ sub register {
   $app->helper(l => sub { shift->stash->{i18n}->localize(@_) });
 }
 
-# Container
 package Mojolicious::Plugin::I18n::_Handler;
 use Mojo::Base -base;
 
 # "Robot 1-X, save my friends! And Zoidberg!"
 sub languages {
   my ($self, @languages) = @_;
-
   return $self->{_language} unless @languages;
 
   # Handle
@@ -68,16 +65,14 @@ sub languages {
     $self->{_language} = $handle->language_tag;
   }
 
-  return $self;
+  $self;
 }
 
 sub localize {
   my $self = shift;
   my $key  = shift;
-
-  # Localize
   return $key unless my $handle = $self->{_handle};
-  return $handle->maketext($key, @_);
+  $handle->maketext($key, @_);
 }
 
 1;
@@ -19,7 +19,7 @@ sub parse {
   die qq/Couldn't parse config "$file": $error/ if !$config && $error;
   die qq/Invalid config "$file"./ if !$config || ref $config ne 'HASH';
 
-  return $config;
+  $config;
 }
 
 sub register {
@@ -49,7 +49,7 @@ sub render {
   $content = $mt->render($content, $app);
   utf8::encode $content;
 
-  return $content;
+  $content;
 }
 
 1;
@@ -0,0 +1,80 @@
+package Mojolicious::Plugin::Mount;
+use Mojo::Base 'Mojolicious::Plugin';
+
+use Mojo::Server;
+
+sub register {
+  my ($self, $app, $conf) = @_;
+
+  # Extract host and path
+  my $prefix = (keys %$conf)[0];
+  my ($host, $path);
+  if ($prefix =~ /^(\*\.)?([^\/]+)(\/.*)?$/) {
+    $host = quotemeta $2;
+    $host = "(?:.*\\.)?$host" if $1;
+    $path = $3;
+    $path = '/' unless defined $path;
+    $host = qr/^$host$/i;
+    $app->routes->cache(0);
+  }
+  else { $path = $prefix }
+
+  # Generate route
+  my $route =
+    $app->routes->route($path)
+    ->detour(app => Mojo::Server->new->load_app($conf->{$prefix}));
+  $route->over(host => $host) if $host;
+
+  $route;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Plugin::Mount - Application Mount Plugin
+
+=head1 SYNOPSIS
+
+  # Mojolicious
+  $self->plugin(mount => {'/prefix' => '/home/sri/myapp.pl'});
+
+  # Mojolicious::Lite
+  plugin mount => {'/prefix' => '/home/sri/myapp.pl'};
+
+  # Adjust the generated route
+  my $example = plugin mount => {'/example' => '/home/sri/example.pl'};
+  $example->to(message => 'It works great!');
+
+  # Mount application with host (automatically disables route caching)
+  plugin mount => {'mojolicio.us' => '/home/sri/myapp.pl'};
+
+  # Host and path
+  plugin mount => {'mojolicio.us/myapp' => '/home/sri/myapp.pl'};
+
+  # Or even hosts with wildcard subdomains
+  plugin mount => {'*.mojolicio.us/myapp' => '/home/sri/myapp.pl'};
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Plugin::Mount> is a plugin that allows you to mount whole
+L<Mojolicious> applications.
+Note that this module is EXPERIMENTAL and might change without warning!
+
+=head1 METHODS
+
+L<Mojolicious::Plugin::Mount> inherits all methods from
+L<Mojolicious::Plugin> and implements the following new ones.
+
+=head2 C<register>
+
+  $plugin->register;
+
+Mount L<Mojolicious> application.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
+
+=cut
@@ -114,10 +114,10 @@ sub register {
           url_escape $anchor, 'A-Za-z0-9_';
           $anchor =~ s/\%//g;
           push @$sections, [] if $tag->type eq 'h1' || !@$sections;
-          push @{$sections->[-1]}, $text, "/$url#$anchor";
+          push @{$sections->[-1]}, $text, $url->fragment($anchor)->to_abs;
           $tag->replace_content(
             $self->link_to(
-              $text => "/$url#toc",
+              $text => $url->fragment('toc')->to_abs,
               class => 'mojoscroll',
               id    => $anchor
             )
@@ -160,14 +160,14 @@ sub _pod_to_html {
   # Parse
   my $output;
   $parser->output_string(\$output);
-  eval { $parser->parse_string_document($pod) };
+  eval { $parser->parse_string_document("$pod") };
   return $@ if $@;
 
   # Filter
   $output =~ s/<a name='___top' class='dummyTopAnchor'\s*?><\/a>\n//g;
   $output =~ s/<a class='u'.*?name=".*?"\s*>(.*?)<\/a>/$1/sg;
 
-  return $output;
+  $output;
 }
 
 1;
@@ -13,16 +13,14 @@ sub register {
     after_static_dispatch => sub {
       my $self = shift;
 
-      # New request
-      my $stash  = $self->stash;
+      # Ignore static files
+      my $stash = $self->stash;
+      return if $stash->{'mojo.static'} || $stash->{'mojo.started'};
       my $req    = $self->req;
       my $method = $req->method;
       my $path   = $req->url->path->to_abs_string;
       my $ua     = $req->headers->user_agent || 'Anonymojo';
-      $self->app->log->debug("$method $path ($ua).")
-        unless $stash->{'mojo.static'};
-
-      # Start
+      $self->app->log->debug("$method $path ($ua).");
       $stash->{'mojo.started'} = [Time::HiRes::gettimeofday()];
     }
   );
@@ -32,17 +30,17 @@ sub register {
     after_dispatch => sub {
       my $self = shift;
 
-      # Time
+      # Ignore static files
       my $stash = $self->stash;
-      return unless my $started = $stash->{'mojo.started'};
+      return unless my $started = delete $stash->{'mojo.started'};
+      return if $stash->{'mojo.static'};
       my $elapsed = sprintf '%f',
         Time::HiRes::tv_interval($started, [Time::HiRes::gettimeofday()]);
       my $rps     = $elapsed == 0 ? '??' : sprintf '%.3f', 1 / $elapsed;
       my $res     = $self->res;
       my $code    = $res->code || 200;
       my $message = $res->message || $res->default_message($code);
-      $self->app->log->debug("$code $message (${elapsed}s, $rps/s).")
-        unless $stash->{'mojo.static'};
+      $self->app->log->debug("$code $message (${elapsed}s, $rps/s).");
     }
   );
 }
@@ -1,6 +1,7 @@
 package Mojolicious::Plugin::TagHelpers;
 use Mojo::Base 'Mojolicious::Plugin';
 
+use List::Util 'first';
 use Mojo::ByteStream 'b';
 use Mojo::Util 'xml_escape';
 
@@ -182,7 +183,7 @@ sub register {
             else { $parts .= $cb->($o) }
           }
 
-          return $parts;
+          $parts;
         }
       );
     }
@@ -272,25 +273,22 @@ sub _input {
   else { %attrs = @_ }
 
   # Value
-  my $p = $c->param($name);
+  my @p = $c->param($name);
 
+  # Special selection value
   my $t = $attrs{type} || '';
-  if (defined $p && $t ne 'submit') {
+  if (@p && $t ne 'submit') {
 
-    # Checkbox
-    if ($t eq 'checkbox') {
-      $attrs{checked} = 'checked';
-    }
-
-    # Radiobutton
-    elsif ($t eq 'radio') {
-      my $value = $attrs{value};
-      $value = '' unless defined $value;
-      $attrs{checked} = 'checked' if $value eq $p;
+    # Checkbox or radiobutton
+    my $value = $attrs{value};
+    $value = '' unless defined $value;
+    if ($t eq 'checkbox' || $t eq 'radio') {
+      $attrs{value} = $value;
+      $attrs{checked} = 'checked' if defined first { $value eq $_ } @p;
     }
 
     # Other
-    else { $attrs{value} = $p }
+    else { $attrs{value} = $p[0] }
 
     return $self->_tag('input', name => $name, %attrs);
   }
@@ -331,7 +329,7 @@ sub _tag {
   else { $tag .= ' />' }
 
   # Prevent escaping
-  return b($tag);
+  b($tag);
 }
 
 1;
@@ -596,6 +594,15 @@ HTML5 tag generator.
   <div id="foo" />
   <div>Content</div>
 
+Very useful for reuse in more specific tag helpers.
+
+  $self->tag('div');
+  $self->tag('div', id => 'foo');
+  $self->tag(div => sub { 'Content' });
+
+Results are automatically wrapped in L<Mojo::ByteStream> objects to prevent
+accidental double escaping.
+
 =head2 C<text_field>
 
   <%= text_field 'first_name' %>
@@ -13,7 +13,7 @@ sub add_hook {
   return $self unless $name && $cb;
   $self->hooks->{$name} ||= [];
   push @{$self->hooks->{$name}}, $cb;
-  return $self;
+  $self;
 }
 
 # "Also you have a rectangular object in your colon.
@@ -33,11 +33,7 @@ sub load_plugin {
 
     # Try all namspaces
     for my $namespace (@{$self->namespaces}) {
-
-      # Module
       my $module = "${namespace}::$class";
-
-      # Load and register
       return $module->new if $self->_load($module);
     }
   }
@@ -51,7 +47,7 @@ sub register_plugin {
   my $self = shift;
   my $name = shift;
   my $app  = shift;
-  return $self->load_plugin($name)->register($app, ref $_[0] ? $_[0] : {@_});
+  $self->load_plugin($name)->register($app, ref $_[0] ? $_[0] : {@_});
 }
 
 sub run_hook {
@@ -59,7 +55,7 @@ sub run_hook {
   return $self unless my $name  = shift;
   return $self unless my $hooks = $self->hooks->{$name};
   for my $hook (@$hooks) { $hook->(@_) }
-  return $self;
+  $self;
 }
 
 # "Everybody's a jerk. You, me, this jerk."
@@ -68,20 +64,21 @@ sub run_hook_reverse {
   return $self unless my $name  = shift;
   return $self unless my $hooks = $self->hooks->{$name};
   for my $hook (reverse @$hooks) { $hook->(@_) }
-  return $self;
+  $self;
 }
 
 sub _load {
   my ($self, $module) = @_;
 
   # Load
-  my $e = Mojo::Loader->load($module);
-  if (ref $e) { die $e }
-  return if $e;
+  if (my $e = Mojo::Loader->load($module)) {
+    die $e if ref $e;
+    return;
+  }
 
   # Module is a plugin
   return unless $module->can('new') && $module->can('register');
-  return 1;
+  1;
 }
 
 1;
@@ -49,19 +49,19 @@ sub new {
     }
   );
 
-  return $self;
+  $self;
 }
 
 sub add_handler {
   my ($self, $name, $cb) = @_;
   $self->handlers->{$name} = $cb;
-  return $self;
+  $self;
 }
 
 sub add_helper {
   my ($self, $name, $cb) = @_;
   $self->helpers->{$name} = $cb;
-  return $self;
+  $self;
 }
 
 sub get_data_template {
@@ -75,17 +75,16 @@ sub render {
   my ($self, $c, $args) = @_;
   $args ||= {};
 
-  my $stash = $c->stash;
-  my $content = $stash->{'mojo.content'} ||= {};
-
   # Localize extends and layout
   my $partial = $args->{partial};
+  my $stash   = $c->stash;
   local $stash->{layout}  = $partial ? undef : $stash->{layout};
   local $stash->{extends} = $partial ? undef : $stash->{extends};
 
   # Merge stash and arguments
   while (my ($key, $value) = each %$args) { $stash->{$key} = $value }
 
+  # Extract important stash values
   my $template = delete $stash->{template};
   my $class    = $stash->{template_class};
   my $format   = $stash->{format} || $self->default_format;
@@ -94,6 +93,8 @@ sub render {
   my $json     = delete $stash->{json};
   my $text     = delete $stash->{text};
   my $inline   = delete $stash->{inline};
+
+  # Pick handler
   $handler = $self->default_handler if defined $inline && !defined $handler;
   my $options = {
     template       => $template,
@@ -103,9 +104,10 @@ sub render {
     inline         => $inline,
     template_class => $class
   };
-  my $output;
 
   # Text
+  my $output;
+  my $content = $stash->{'mojo.content'} ||= {};
   if (defined $text) {
     $self->handlers->{text}->($self, $c, \$output, {text => $text});
     $content->{content} = b("$output")
@@ -154,7 +156,7 @@ sub render {
     encode $encoding, $output if $encoding && $output && !$json && !$data;
   }
 
-  return ($output, $c->app->types->type($format) || 'text/plain');
+  ($output, $c->app->types->type($format) || 'text/plain');
 }
 
 sub template_name {
@@ -162,18 +164,17 @@ sub template_name {
 
   return unless my $template = $options->{template} || '';
   return unless my $format = $options->{format};
-
   my $handler = $options->{handler};
   my $file    = "$template.$format";
   $file = "$file.$handler" if defined $handler;
 
-  return $file;
+  $file;
 }
 
 sub template_path {
   my $self = shift;
   return unless my $name = $self->template_name(shift);
-  return File::Spec->catfile($self->root, split '/', $name);
+  File::Spec->catfile($self->root, split '/', $name);
 }
 
 sub _detect_handler {
@@ -201,14 +202,13 @@ sub _detect_handler {
     if ($template =~ /^$file\.(\w+)$/) { return $1 }
   }
 
-  return;
+  undef;
 }
 
 # "You are hereby conquered.
 #  Please line up in order of how much beryllium it takes to kill you."
 sub _detect_template_class {
   my ($self, $options) = @_;
-  return
        $options->{template_class}
     || $ENV{MOJO_TEMPLATE_CLASS}
     || $self->default_template_class
@@ -221,13 +221,13 @@ sub _extends {
   if (my $layout = delete $stash->{layout}) {
     $stash->{extends} ||= $self->layout_prefix . '/' . $layout;
   }
-  return delete $stash->{extends};
+  delete $stash->{extends};
 }
 
 sub _list_data_templates {
   my ($self, $class) = @_;
   my $all = Mojo::Command->new->get_all_data($class);
-  return [keys %$all];
+  [keys %$all];
 }
 
 # "Well, at least here you'll be treated with dignity.
@@ -253,7 +253,7 @@ sub _render_template {
   return unless $renderer->($self, $c, $output, $options);
 
   # Success!
-  return 1;
+  1;
 }
 
 1;
@@ -25,7 +25,7 @@ sub new {
   # WebSocket
   $self->{_websocket} = shift;
 
-  return $self;
+  $self;
 }
 
 # "Life can be hilariously cruel."
@@ -33,16 +33,12 @@ sub match {
   my ($self, $r, $c) = @_;
   return unless $r;
 
-  my $dictionary = $self->{_dictionary} ||= $r->dictionary;
-
-  # Root
-  $self->root($r) unless $self->root;
-
-  my $path    = $self->{_path};
-  my $pattern = $r->pattern;
-
   # Match
-  my $captures = $pattern->shape_match(\$path);
+  $self->root($r) unless $self->root;
+  my $dictionary = $self->{_dictionary} ||= $r->dictionary;
+  my $path       = $self->{_path};
+  my $pattern    = $r->pattern;
+  my $captures   = $pattern->shape_match(\$path);
   return unless $captures;
   $self->{_path} = $path;
 
@@ -80,21 +76,14 @@ sub match {
   my $empty = !length $path || $path eq '/' ? 1 : 0;
 
   # Partial
-  if (my $partial = $r->partial) {
-    $captures->{$partial} = $path;
+  if ($r->partial) {
+    $captures->{path} = $path;
     $self->endpoint($r);
     $empty = 1;
   }
 
-  # Format
-  my $endpoint = $r->is_endpoint;
-  if ($endpoint && !$pattern->format && $path =~ /^\/?\.([^\/]+)$/) {
-    $captures->{format} = $1;
-    $empty = 1;
-  }
-  $captures->{format} ||= $pattern->format if $pattern->format;
-
   # Update stack
+  my $endpoint = $r->is_endpoint;
   if ($r->inline || ($endpoint && $empty)) {
     push @{$self->stack}, {%$captures};
     delete $captures->{cb};
@@ -113,8 +102,6 @@ sub match {
   # Match children
   my $snapshot = [@{$self->stack}];
   for my $child (@{$r->children}) {
-
-    # Match
     $self->match($child, $c);
 
     # Endpoint found
@@ -131,7 +118,7 @@ sub match {
     }
   }
 
-  return $self;
+  $self;
 }
 
 sub path_for {
@@ -206,11 +193,17 @@ sub path_for {
 
   # Merge values
   $values = {%$captures, format => undef, %$values};
+  my $pattern = $endpoint->pattern;
+  $values->{format} =
+    defined $captures->{format}
+    ? $captures->{format}
+    : $pattern->defaults->{format}
+    if $pattern->reqs->{format};
 
   # Render
   my $path = $endpoint->render('', $values);
   utf8::downgrade $path, 1;
-  return wantarray ? ($path, $endpoint->has_websocket) : $path;
+  wantarray ? ($path, $endpoint->has_websocket) : $path;
 }
 
 1;
@@ -16,20 +16,14 @@ has [qw/format pattern regex/];
 sub new {
   my $self = shift->SUPER::new();
   $self->parse(@_);
-  return $self;
+  $self;
 }
 
 sub match {
   my ($self, $path) = @_;
-
-  # Match
   my $result = $self->shape_match(\$path);
-
-  # Endpoint
   return $result if !$path || $path eq '/';
-
-  # Partial or no match
-  return;
+  undef;
 }
 
 sub parse {
@@ -40,18 +34,22 @@ sub parse {
   return $self if !defined $pattern || $pattern eq '/';
   $pattern = "/$pattern" unless $pattern =~ /^\//;
 
-  # Format
-  if ($pattern =~ /\.([^\/\)]+)$/) { $self->format($1) }
-
   # Requirements
   my $reqs = ref $_[0] eq 'HASH' ? $_[0] : {@_};
   $self->reqs($reqs);
 
+  # Format in pattern
+  if ($pattern =~ s/\.([^\/\)]+)$//) {
+    $reqs->{format}           = quotemeta $1;
+    $self->defaults->{format} = $1;
+    $self->{_strict}          = 1;
+  }
+
   # Tokenize
   $self->pattern($pattern);
   $self->_tokenize;
 
-  return $self;
+  $self;
 }
 
 sub render {
@@ -61,6 +59,7 @@ sub render {
   $values ||= {};
   $values = {%{$self->defaults}, %$values};
 
+  # Turn pattern into path
   my $string   = '';
   my $optional = 1;
   for my $token (reverse @{$self->tree}) {
@@ -83,10 +82,8 @@ sub render {
       my $name = $token->[1];
       $rendered = $values->{$name};
       $rendered = '' unless defined $rendered;
-
       my $default = $self->defaults->{$name};
       $default = '' unless defined $default;
-
       $optional = 0 unless $default eq $rendered;
       $rendered = '' if $optional && $default eq $rendered;
     }
@@ -94,7 +91,7 @@ sub render {
     $string = "$rendered$string";
   }
 
-  return $string || '/';
+  $string || '/';
 }
 
 sub shape_match {
@@ -117,18 +114,38 @@ sub shape_match {
       my $capture = shift @captures;
       $result->{$symbol} = $capture if defined $capture;
     }
+
+    # Format
+    my $format = $self->format;
+    if (defined $format && $$pathref =~ s/$format//) {
+      $result->{format} ||= $1;
+    }
+    elsif ($self->reqs->{format}) {
+      return if !$result->{format} || $self->{_strict};
+    }
+
     return $result;
   }
 
-  return;
+  undef;
 }
 
 sub _compile {
   my $self = shift;
 
+  # Compile format regular expression
+  my $reqs = $self->reqs;
+  if (!exists $reqs->{format} || defined $reqs->{format}) {
+    my $format =
+      defined $reqs->{format} ? _compile_req($reqs->{format}) : '([^\/]+)';
+    $self->format(qr/^\/?\.$format$/);
+  }
+
+  # Compile tree to regular expression
   my $block    = '';
   my $regex    = '';
   my $optional = 1;
+  my $defaults = $self->defaults;
   for my $token (reverse @{$self->tree}) {
     my $op       = $token->[0];
     my $compiled = '';
@@ -138,10 +155,8 @@ sub _compile {
 
       # Full block
       $block = $optional ? "(?:/$block)?" : "/$block";
-
       $regex = "$block$regex";
       $block = '';
-
       next;
     }
 
@@ -154,7 +169,6 @@ sub _compile {
     # Symbol
     elsif ($op eq 'relaxed' || $op eq 'symbol' || $op eq 'wildcard') {
       my $name = $token->[1];
-
       unshift @{$self->symbols}, $name;
 
       # Relaxed
@@ -166,11 +180,12 @@ sub _compile {
       # Wildcard
       elsif ($op eq 'wildcard') { $compiled = '(.+)' }
 
-      my $req = $self->reqs->{$name};
-      $compiled = "($req)" if $req;
-
-      $optional = 0 unless exists $self->defaults->{$name};
+      # Custom regex
+      my $req = $reqs->{$name};
+      $compiled = _compile_req($req) if $req;
 
+      # Optional placeholder
+      $optional = 0 unless exists $defaults->{$name};
       $compiled .= '?' if $optional;
     }
 
@@ -181,25 +196,34 @@ sub _compile {
   # Not rooted with a slash
   $regex = "$block$regex" if $block;
 
+  # Compile
   $regex = qr/^$regex/;
   $self->regex($regex);
 
-  return $regex;
+  $regex;
+}
+
+sub _compile_req {
+  my $req = shift;
+  return "($req)" if !ref $req || ref $req ne 'ARRAY';
+  '(' . join('|', @$req) . ')';
 }
 
 sub _tokenize {
   my $self = shift;
 
-  my $pattern        = $self->pattern;
+  # Token
   my $quote_end      = $self->quote_end;
   my $quote_start    = $self->quote_start;
   my $relaxed_start  = $self->relaxed_start;
   my $symbol_start   = $self->symbol_start;
   my $wildcard_start = $self->wildcard_start;
-  my $tree           = [];
-  my $state          = 'text';
-  my $quoted         = 0;
 
+  # Parse the pattern character wise
+  my $pattern = $self->pattern;
+  my $tree    = [];
+  my $state   = 'text';
+  my $quoted  = 0;
   while (length(my $char = substr $pattern, 0, 1, '')) {
 
     # Inside a symbol
@@ -261,7 +285,6 @@ sub _tokenize {
 
     # Text
     else {
-
       $state = 'text';
 
       # New text element
@@ -276,7 +299,7 @@ sub _tokenize {
   }
   $self->tree($tree);
 
-  return $self;
+  $self;
 }
 
 1;
@@ -306,6 +329,14 @@ L<Mojolicious::Routes::Pattern> implements the following attributes.
 
 Default parameters.
 
+=head2 C<format>
+
+  my $regex = $pattern->format;
+  $pattern  = $pattern->format($regex);
+
+Compiled regex for format matching.
+Note that this attribute is EXPERIMENTAL and might change without warning!
+
 =head2 C<pattern>
 
   my $pattern = $pattern->pattern;
@@ -330,7 +361,7 @@ Character indicating the start of a quoted placeholder, defaults to C<(>.
 =head2 C<regex>
 
   my $regex = $pattern->regex;
-  $pattern  = $pattern->regex(qr/\/foo/);
+  $pattern  = $pattern->regex($regex);
 
 Pattern in compiled regex form.
 
@@ -28,7 +28,7 @@ sub AUTOLOAD {
   # Call shortcut
   Carp::croak(qq/Can't locate object method "$method" via package "$package"/)
     unless my $shortcut = $self->shortcuts->{$method};
-  return $self->$shortcut(@_);
+  $self->$shortcut(@_);
 }
 
 sub DESTROY { }
@@ -36,7 +36,7 @@ sub DESTROY { }
 sub new {
   my $self = shift->SUPER::new();
   $self->parse(@_);
-  return $self;
+  $self;
 }
 
 sub add_child {
@@ -46,25 +46,25 @@ sub add_child {
   $route->parent($self);
   weaken $route->{parent};
 
-  # Shortcuts
+  # Inherit shortcuts
   $route->shortcuts($self->shortcuts);
 
   # Add to tree
   push @{$self->children}, $route;
 
-  return $self;
+  $self;
 }
 
 sub add_condition {
   my ($self, $name, $cb) = @_;
   $self->dictionary->{$name} = $cb;
-  return $self;
+  $self;
 }
 
 sub add_shortcut {
   my ($self, $name, $cb) = @_;
   $self->shortcuts->{$name} = $cb;
-  return $self;
+  $self;
 }
 
 sub any {
@@ -88,7 +88,7 @@ sub auto_render {
     1;
   } or $c->render_exception($@);
 
-  return;
+  1;
 }
 
 sub bridge { shift->route(@_)->inline(1) }
@@ -97,9 +97,9 @@ sub del { shift->_generate_route('delete', @_) }
 
 sub detour {
   my $self = shift;
-  $self->partial('path');
+  $self->partial(1);
   $self->to(@_);
-  return $self;
+  $self;
 }
 
 sub dispatch {
@@ -119,7 +119,7 @@ sub dispatch {
 
   # Cached
   my $cache = $self->cache;
-  if (my $cached = $cache->get("$method:$path:$websocket")) {
+  if ($cache && (my $cached = $cache->get("$method:$path:$websocket"))) {
     $m->root($self);
     $m->stack($cached->{stack});
     $m->captures($cached->{captures});
@@ -131,7 +131,7 @@ sub dispatch {
     $m->match($self, $c);
 
     # Endpoint found
-    if (my $endpoint = $m->endpoint) {
+    if ($cache && (my $endpoint = $m->endpoint)) {
 
       # Cache routes without conditions
       $cache->set(
@@ -145,13 +145,13 @@ sub dispatch {
   }
 
   # No match
-  return 1 unless $m && @{$m->stack};
+  return unless $m && @{$m->stack};
 
   # Walk the stack
-  return 1 if $self->_walk_stack($c);
+  return if $self->_walk_stack($c);
 
   # Render
-  return $self->auto_render($c);
+  $self->auto_render($c);
 }
 
 sub get { shift->_generate_route('get', @_) }
@@ -160,19 +160,19 @@ sub has_conditions {
   my $self = shift;
   return 1 if @{$self->conditions};
   if (my $parent = $self->parent) { return $parent->has_conditions }
-  return;
+  undef;
 }
 
 sub has_custom_name {
   return 1 if shift->{_custom};
-  return;
+  undef;
 }
 
 sub has_websocket {
   my $self = shift;
   return 1 if $self->is_websocket;
   if (my $parent = $self->parent) { return $parent->is_websocket }
-  return;
+  undef;
 }
 
 sub hide { push @{shift->hidden}, @_ }
@@ -182,12 +182,12 @@ sub is_endpoint {
   return   if $self->inline;
   return 1 if $self->block;
   return   if @{$self->children};
-  return 1;
+  1;
 }
 
 sub is_websocket {
   return 1 if shift->{_websocket};
-  return;
+  undef;
 }
 
 sub name {
@@ -204,7 +204,7 @@ sub name {
   elsif (@_) { return $self }
 
   # Name
-  return $self->{_name};
+  $self->{_name};
 }
 
 sub over {
@@ -212,7 +212,7 @@ sub over {
   return $self unless @_;
   my $conditions = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
   push @{$self->conditions}, @$conditions;
-  return $self;
+  $self;
 }
 
 sub parse {
@@ -228,7 +228,7 @@ sub parse {
   $self->{_name}   = $name;
   $self->{_custom} = 0;
 
-  return $self;
+  $self;
 }
 
 sub post { shift->_generate_route('post', @_) }
@@ -253,16 +253,14 @@ sub render {
   # Parent
   $path = $self->parent->render($path, $values) if $self->parent;
 
-  return $path;
+  $path;
 }
 
-# "Morbo forget how you spell that letter that looks like a man wearing a hat.
-#  Hello, tiny man. I will destroy you!"
 sub route {
   my $self  = shift;
   my $route = $self->new(@_);
   $self->add_child($route);
-  return $route;
+  $route;
 }
 
 sub to {
@@ -316,21 +314,19 @@ sub to {
     }
   }
 
-  # Pattern
-  my $pattern = $self->pattern;
-
   # Defaults
-  my $old = $pattern->defaults;
+  my $pattern = $self->pattern;
+  my $old     = $pattern->defaults;
   $pattern->defaults({%$old, %$defaults}) if $defaults;
 
-  return $self;
+  $self;
 }
 
 sub to_string {
   my $self = shift;
   my $pattern = $self->parent ? $self->parent->to_string : '';
   $pattern .= $self->pattern->pattern if $self->pattern->pattern;
-  return $pattern;
+  $pattern;
 }
 
 sub under { shift->_generate_route('under', @_) }
@@ -346,7 +342,7 @@ sub via {
   }
 
   # Get
-  return $self->{_via};
+  $self->{_via};
 }
 
 sub waypoint { shift->route(@_)->block(1) }
@@ -355,7 +351,7 @@ sub websocket {
   my $self  = shift;
   my $route = $self->any(@_);
   $route->{_websocket} = 1;
-  return $route;
+  $route;
 }
 
 sub _dispatch_callback {
@@ -374,7 +370,7 @@ sub _dispatch_callback {
   }
 
   return 1 if !$staging || $continue;
-  return;
+  undef;
 }
 
 sub _dispatch_controller {
@@ -384,10 +380,9 @@ sub _dispatch_controller {
   return 1
     unless my $app = $field->{app} || $self->_generate_class($field, $c);
   my $method = $self->_generate_method($field, $c);
-
   my $dispatch = ref $app || $app;
   $dispatch .= "->$method" if $method;
-  $c->app->log->debug("Dispatching $dispatch.");
+  $c->app->log->debug(qq/Dispatching "$dispatch"./);
 
   # Load class
   if (!ref $app && !$self->{_loaded}->{$app}) {
@@ -424,7 +419,12 @@ sub _dispatch_controller {
       }
 
       # Render
-      elsif (!$staging) { $self->auto_render($app) }
+      else {
+        $c->app->log->debug(
+          qq/Action "$dispatch" not found, assuming template without action./
+        );
+        $self->auto_render($app) unless $staging;
+      }
 
       # Merge stash
       my $new = $app->stash;
@@ -458,7 +458,7 @@ sub _dispatch_controller {
   }
 
   return 1 if !$staging || $continue;
-  return;
+  undef;
 }
 
 sub _generate_class {
@@ -482,7 +482,7 @@ sub _generate_class {
   # Invalid
   return unless $class =~ /^[a-zA-Z0-9_:]+$/;
 
-  return $class;
+  $class;
 }
 
 sub _generate_method {
@@ -494,9 +494,8 @@ sub _generate_method {
     $self->{_hidden}->{$_}++ for @{$self->hidden};
   }
 
-  return unless my $method = $field->{method} || $field->{action};
-
   # Hidden
+  return unless my $method = $field->{method} || $field->{action};
   if ($self->{_hidden}->{$method} || index($method, '_') == 0) {
     $c->app->log->debug(qq/Action "$method" is not allowed./);
     return;
@@ -508,16 +507,15 @@ sub _generate_method {
     return;
   }
 
-  return $method;
+  $method;
 }
 
 sub _generate_route {
   my ($self, $methods, @args) = @_;
 
+  # Route information
   my ($cb, $constraints, $defaults, $name, $pattern);
   my $conditions = [];
-
-  # Route information
   while (defined(my $arg = shift @args)) {
 
     # First scalar is the pattern
@@ -543,9 +541,7 @@ sub _generate_route {
 
   # Defaults
   $constraints ||= [];
-
-  # Defaults
-  $defaults ||= {};
+  $defaults    ||= {};
   $defaults->{cb} = $cb if $cb;
 
   # Create bridge
@@ -558,7 +554,7 @@ sub _generate_route {
     $self->route($pattern, {@$constraints})->over($conditions)->via($methods)
     ->to($defaults)->name($name);
 
-  return $route;
+  $route;
 }
 
 sub _walk_stack {
@@ -568,12 +564,11 @@ sub _walk_stack {
   local $SIG{__DIE__} =
     sub { ref $_[0] ? CORE::die($_[0]) : Mojo::Exception->throw(@_) };
 
-  my $stack = $c->match->stack;
-  my $stash = $c->stash;
-  $stash->{'mojo.captures'} ||= {};
-
   # Walk the stack
+  my $stack   = $c->match->stack;
+  my $stash   = $c->stash;
   my $staging = @$stack;
+  $stash->{'mojo.captures'} ||= {};
   for my $field (@$stack) {
     $staging--;
 
@@ -601,7 +596,7 @@ sub _walk_stack {
   }
 
   # Done
-  return;
+  undef;
 }
 
 1;
@@ -685,6 +680,10 @@ The children of this routes object, used for nesting routes.
 Routing cache, by default a L<Mojo::Cache> object.
 Note that this attribute is EXPERIMENTAL and might change without warning!
 
+  $r->cache(0);
+
+Route caching can also be disabled with a false value.
+
 =head2 C<conditions>
 
   my $conditions  = $r->conditions;
@@ -738,10 +737,9 @@ The parent of this route, used for nesting routes.
 =head2 C<partial>
 
   my $partial = $r->partial;
-  $r          = $r->partial('path');
+  $r          = $r->partial(1);
 
-Route has no specific end, remaining characters will be captured with the
-partial name.
+Route has no specific end, remaining characters will be captured in C<path>.
 
 =head2 C<pattern>
 
@@ -835,7 +833,7 @@ application embedding.
 
 =head2 C<dispatch>
 
-  my $e = $r->dispatch(Mojolicious::Controller->new);
+  my $success = $r->dispatch(Mojolicious::Controller->new);
 
 Match routes and dispatch.
 
@@ -959,8 +957,8 @@ Stringifies the whole route.
 
 =head2 C<under>
 
-  my $under = $route->under(sub {...});
-  my $under = $route->under('/:foo');
+  my $under = $r->under(sub {...});
+  my $under = $r->under('/:foo');
 
 Generate bridges.
 See also the L<Mojolicious::Lite> tutorial for more argument variations.
@@ -977,18 +975,31 @@ restrictions.
 
 =head2 C<waypoint>
 
-  my $route = $r->waypoint('/:c/:a', a => qr/\w+/);
+  my $r = $r->waypoint('/:c/:a', a => qr/\w+/);
 
 Add a waypoint to this route as nested child.
 
 =head2 C<websocket>
 
-  my $websocket = $route->websocket('/:foo' => sub {...});
+  my $websocket = $r->websocket('/:foo' => sub {...});
 
 Generate route matching only C<WebSocket> handshakes.
 See also the L<Mojolicious::Lite> tutorial for more argument variations.
 Note that this method is EXPERIMENTAL and might change without warning!
 
+=head1 SHORTCUTS
+
+In addition to the attributes and methods above you can also call shortcuts
+on instances of L<Mojolicious::Routes>.
+
+  $r->add_shortcut(firefox => sub {
+    my ($r, $path) = @_;
+    $r->get($path, agent => qr/Firefox/);
+  });
+
+  $r->firefox('/welcome')->to('firefox#welcome');
+  $r->firefox('/bye')->to('firefox#bye);
+
 =head1 SEE ALSO
 
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
@@ -20,11 +20,9 @@ sub load {
   # Session cookie
   return unless my $value = $c->signed_cookie($self->cookie_name);
 
-  # Decode
+  # Deserialize
   $value =~ s/\-/\=/g;
   b64_decode $value;
-
-  # Deserialize
   return unless my $session = $JSON->decode($value);
 
   # Expiration
@@ -69,8 +67,6 @@ sub store {
 
     # Serialize
     $value = $JSON->encode($session);
-
-    # Encode
     b64_encode $value, '';
     $value =~ s/\=/\-/g;
   }
@@ -17,26 +17,29 @@ sub dispatch {
   my ($self, $c) = @_;
 
   # Already rendered
-  return if $c->res->code;
+  return 1 if $c->res->code;
 
   # Canonical path
-  my $path = $c->req->url->path->clone->canonicalize->to_string;
+  my $stash = $c->stash;
+  my $path  = $stash->{path};
+  $path = $c->req->url->path->clone->canonicalize->to_string
+    unless defined $path;
 
   # Split parts
   my @parts = @{Mojo::Path->new->parse($path)->parts};
-  return 1 unless @parts;
+  return unless @parts;
 
   # Prevent directory traversal
-  return 1 if $parts[0] eq '..';
+  return if $parts[0] eq '..';
 
   # Serve static file
-  unless ($self->serve($c, join('/', @parts))) {
-    $c->stash->{'mojo.static'} = 1;
+  if ($self->serve($c, join('/', @parts))) {
+    $stash->{'mojo.static'} = 1;
     $c->rendered;
-    return;
+    return 1;
   }
 
-  return 1;
+  1;
 }
 
 sub serve {
@@ -76,7 +79,7 @@ sub serve {
       # Exists, but is forbidden
       else {
         $c->app->log->debug(qq/File "$rel" forbidden./);
-        $res->code(403) and return;
+        $res->code(403) and return 1;
       }
 
       # Done
@@ -84,8 +87,8 @@ sub serve {
     }
   }
 
-  # Inline file
-  if (!$asset && defined(my $file = $self->_get_inline_file($c, $rel))) {
+  # DATA file
+  if (!$asset && defined(my $file = $self->_get_data_file($c, $rel))) {
     $size  = length $file;
     $asset = Mojo::Asset::Memory->new->add_chunk($file);
   }
@@ -105,7 +108,7 @@ sub serve {
         $rsh->remove('Content-Type');
         $rsh->remove('Content-Length');
         $rsh->remove('Content-Disposition');
-        return;
+        return 1;
       }
     }
 
@@ -126,7 +129,7 @@ sub serve {
 
         # Not satisfiable
         $res->code(416);
-        return;
+        return 1;
       }
     }
     $asset->start_range($start);
@@ -138,35 +141,33 @@ sub serve {
     $rsh->content_type($c->app->types->type($ext) || 'text/plain');
     $rsh->accept_ranges('bytes');
     $rsh->last_modified(Mojo::Date->new($modified));
-    return;
+    return 1;
   }
 
-  return 1;
+  undef;
 }
 
-sub _get_inline_file {
+sub _get_data_file {
   my ($self, $c, $rel) = @_;
 
   # Protect templates
   return if $rel =~ /\.\w+\.\w+$/;
 
-  # Class
+  # Detect DATA class
   my $class =
        $c->stash->{static_class}
     || $ENV{MOJO_STATIC_CLASS}
     || $self->default_static_class
     || 'main';
 
-  # Inline files
-  my $inline = $self->{_inline_files}->{$class}
+  # Find DATA file
+  my $data = $self->{_data_files}->{$class}
     ||= [keys %{Mojo::Command->new->get_all_data($class) || {}}];
-
-  # Find inline file
-  for my $path (@$inline) {
+  for my $path (@$data) {
     return Mojo::Command->new->get_data($path, $class) if $path eq $rel;
   }
 
-  return;
+  undef;
 }
 
 1;
@@ -36,7 +36,7 @@ sub type {
     $self->types->{$ext} = $type;
     return $self;
   }
-  return $self->types->{$ext || ''};
+  $self->types->{$ext || ''};
 }
 
 1;
@@ -1 +1 @@
-.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun{color:#660}.pln{color:#000}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec{color:#606}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}@media print{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun{color:#440}.pln{color:#000}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}
\ No newline at end of file
+.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
\ No newline at end of file
@@ -1,2 +1,2 @@
-PR.registerLangHandler(PR.createSimpleLexer([["com",/^#[^\r\n]*/,null,"#"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\s/,
-null],["typ",/^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[SE]?BANK\=?|BLOCK|BNKSUM|E?CADR|COUNT\*?|2?DEC\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\s/,null],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[!-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),["apollo","agc","aea"])
\ No newline at end of file
+PR.registerLangHandler(PR.createSimpleLexer([["com",/^#[^\n\r]*/,null,"#"],["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r Â\xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,null,'"']],[["kwd",/^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\s/,
+null],["typ",/^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[ES]?BANK=?|BLOCK|BNKSUM|E?CADR|COUNT\*?|2?DEC\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\s/,null],["lit",/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[!-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["apollo","agc","aea"]);
@@ -0,0 +1,18 @@
+/*
+ Copyright (C) 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^[([{]+/,a,"([{"],["clo",/^[)\]}]+/,a,")]}"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:def|if|do|let|quote|var|fn|loop|recur|throw|try|monitor-enter|monitor-exit|defmacro|defn|defn-|macroexpand|macroexpand-1|for|doseq|dosync|dotimes|and|or|when|not|assert|doto|proxy|defstruct|first|rest|cons|defprotocol|deftype|defrecord|reify|defmulti|defmethod|meta|with-meta|ns|in-ns|create-ns|import|intern|refer|alias|namespace|resolve|ref|deref|refset|new|set!|memfn|to-array|into-array|aset|gen-class|reduce|map|filter|find|nil?|empty?|hash-map|hash-set|vec|vector|seq|flatten|reverse|assoc|dissoc|list|list?|disj|get|union|difference|intersection|extend|extend-type|extend-protocol|prn)\b/,a],
+["typ",/^:[\dA-Za-z-]+/]]),["clj"]);
@@ -1,2 +1,2 @@
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[ \t\r\n\f]+/,null," \t\r\n\u000c"]],[["str",/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],["str",/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],["kwd",/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],
-["com",/^(?:<!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#(?:[0-9a-f]{3}){1,2}/i],["pln",/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],["pun",/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^\)\"\']+/]]),["css-str"])
\ No newline at end of file
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com",
+/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);
@@ -0,0 +1 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r Â\xa0"],["pln",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])+(?:'|$)|`[^`]*(?:`|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\/\*[\S\s]*?\*\/)/],["pln",/^(?:[^"'/`]|\/(?![*/]))+/]]),["go"]);
@@ -1,2 +1,2 @@
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\x0B\x0C\r ]+/,null,"\t\n\u000b\u000c\r "],["str",/^\"(?:[^\"\\\n\x0C\r]|\\[\s\S])*(?:\"|$)/,null,'"'],["str",/^\'(?:[^\'\\\n\x0C\r]|\\[^&])\'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+\-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:(?:--+(?:[^\r\n\x0C]*)?)|(?:\{-(?:[^-]|-+[^-\}])*-\}))/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^a-zA-Z0-9\']|$)/,
-null],["pln",/^(?:[A-Z][\w\']*\.)*[a-zA-Z][\w\']*/],["pun",/^[^\t\n\x0B\x0C\r a-zA-Z0-9\'\"]+/]]),["hs"])
\ No newline at end of file
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\r "],["str",/^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["str",/^'(?:[^\n\f\r'\\]|\\[^&])'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:--+[^\n\f\r]*|{-(?:[^-]|-+[^}-])*-})/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^\d'A-Za-z]|$)/,
+null],["pln",/^(?:[A-Z][\w']*\.)*[A-Za-z][\w']*/],["pun",/^[^\d\t-\r "'A-Za-z]+/]]),["hs"]);
@@ -1,2 +1,3 @@
-PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(/,null,"("],["clo",/^\)/,null,")"],["com",/^;[^\r\n]*/,null,";"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,
-null],["lit",/^[+\-]?(?:0x[0-9a-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[ed][+\-]?\d+)?)/i],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[a-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),["cl","el","lisp","scm"])
\ No newline at end of file
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,a,"("],["clo",/^\)+/,a,")"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,a],
+["lit",/^[+-]?(?:[#0]x[\da-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[de][+-]?\d+)?)/i],["lit",/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[_a-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/i],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["cl","el","lisp","scm"]);
@@ -1,2 +1,2 @@
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/,null,"\"'"]],[["com",/^--(?:\[(=*)\[[\s\S]*?(?:\]\1\]|$)|[^\r\n]*)/],["str",/^\[(=*)\[[\s\S]*?(?:\]\1\]|$)/],["kwd",/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],["lit",/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],
-["pln",/^[a-z_]\w*/i],["pun",/^[^\w\t\n\r \xA0][^\w\t\n\r \xA0\"\'\-\+=]*/]]),["lua"])
\ No newline at end of file
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r Â\xa0"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$))/,null,"\"'"]],[["com",/^--(?:\[(=*)\[[\S\s]*?(?:]\1]|$)|[^\n\r]*)/],["str",/^\[(=*)\[[\S\s]*?(?:]\1]|$)/],["kwd",/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],
+["pln",/^[_a-z]\w*/i],["pun",/^[^\w\t\n\r \xa0][^\w\t\n\r "'+=\xa0-]*/]]),["lua"]);
@@ -1,2 +1,2 @@
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["com",/^#(?:if[\t\n\r \xA0]+(?:[a-z_$][\w\']*|``[^\r\n\t`]*(?:``|$))|else|endif|light)/i,null,"#"],["str",/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\r\n]*|\(\*[\s\S]*?\*\))/],["kwd",/^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/],
-["lit",/^[+\-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],["pln",/^(?:[a-z_]\w*[!?#]?|``[^\r\n\t`]*(?:``|$))/i],["pun",/^[^\t\n\r \xA0\"\'\w]+/]]),["fs","ml"])
\ No newline at end of file
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r Â\xa0"],["com",/^#(?:if[\t\n\r \xa0]+(?:[$_a-z][\w']*|``[^\t\n\r`]*(?:``|$))|else|endif|light)/i,null,"#"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])(?:'|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\(\*[\S\s]*?\*\))/],["kwd",/^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/],
+["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],["pln",/^(?:[_a-z][\w']*[!#?]?|``[^\t\n\r`]*(?:``|$))/i],["pun",/^[^\w\t\n\r "'\xa0]+/]]),["fs","ml"]);
@@ -0,0 +1,4 @@
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^(?:'(?:[^\n\r'\\]|\\.)*'|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,a,'"'],["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,a,"#"],["pln",/^\s+/,a," \r\n\t\xa0"]],[["str",/^@"(?:[^"]|"")*(?:"|$)/,a],["str",/^<#[^#>]*(?:#>|$)/,a],["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,a],["com",/^\/\/[^\n\r]*/,a],["com",/^\/\*[\S\s]*?(?:\*\/|$)/,
+a],["kwd",/^(?:abstract|and|as|base|catch|class|def|delegate|enum|event|extern|false|finally|fun|implements|interface|internal|is|macro|match|matches|module|mutable|namespace|new|null|out|override|params|partial|private|protected|public|ref|sealed|static|struct|syntax|this|throw|true|try|type|typeof|using|variant|virtual|volatile|when|where|with|assert|assert2|async|break|checked|continue|do|else|ensures|for|foreach|if|late|lock|new|nolate|otherwise|regexp|repeat|requires|return|surroundwith|unchecked|unless|using|while|yield)\b/,
+a],["typ",/^(?:array|bool|byte|char|decimal|double|float|int|list|long|object|sbyte|short|string|ulong|uint|ufloat|ulong|ushort|void)\b/,a],["lit",/^@[$_a-z][\w$@]*/i,a],["typ",/^@[A-Z]+[a-z][\w$@]*/,a],["pln",/^'?[$_a-z][\w$@]*/i,a],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,a,"0123456789"],["pun",/^.[^\s\w"-$'./@`]*/,a]]),["n","nemerle"]);
@@ -1 +1 @@
-PR.registerLangHandler(PR.sourceDecorator({keywords:"bool bytes default double enum extend extensions false fixed32 fixed64 float group import int32 int64 max message option optional package repeated required returns rpc service sfixed32 sfixed64 sint32 sint64 string syntax to true uint32 uint64",cStyleComments:true}),["proto"])
\ No newline at end of file
+PR.registerLangHandler(PR.sourceDecorator({keywords:"bytes,default,double,enum,extend,extensions,false,group,import,max,message,option,optional,package,repeated,required,returns,rpc,service,syntax,to,true",types:/^(bool|(double|s?fixed|[su]?int)(32|64)|float|string)\b/,cStyleComments:!0}),["proto"]);
@@ -1,2 +1,2 @@
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:(?:""(?:""?(?!")|[^\\"]|\\.)*"{0,3})|(?:[^"\r\n\\]|\\.)*"?))/,null,'"'],["lit",/^`(?:[^\r\n\\`]|\\.)*`?/,null,"`"],["pun",/^[!#%&()*+,\-:;<=>?@\[\\\]^{|}~]+/,null,"!#%&()*+,-:;<=>?@[\\]^{|}~"]],[["str",/^'(?:[^\r\n\\']|\\(?:'|[^\r\n']+))'/],["lit",/^'[a-zA-Z_$][\w$]*(?!['$\w])/],["kwd",/^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\b/],
-["lit",/^(?:true|false|null|this)\b/],["lit",/^(?:(?:0(?:[0-7]+|X[0-9A-F]+))L?|(?:(?:0|[1-9][0-9]*)(?:(?:\.[0-9]+)?(?:E[+\-]?[0-9]+)?F?|L?))|\\.[0-9]+(?:E[+\-]?[0-9]+)?F?)/i],["typ",/^[$_]*[A-Z][_$A-Z0-9]*[a-z][\w$]*/],["pln",/^[$a-zA-Z_][\w$]*/],["com",/^\/(?:\/.*|\*(?:\/|\**[^*/])*(?:\*+\/?)?)/],["pun",/^(?:\.+|\/)/]]),["scala"])
\ No newline at end of file
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r Â\xa0"],["str",/^"(?:""(?:""?(?!")|[^"\\]|\\.)*"{0,3}|(?:[^\n\r"\\]|\\.)*"?)/,null,'"'],["lit",/^`(?:[^\n\r\\`]|\\.)*`?/,null,"`"],["pun",/^[!#%&(--:-@[-^{-~]+/,null,"!#%&()*+,-:;<=>?@[\\]^{|}~"]],[["str",/^'(?:[^\n\r'\\]|\\(?:'|[^\n\r']+))'/],["lit",/^'[$A-Z_a-z][\w$]*(?![\w$'])/],["kwd",/^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\b/],
+["lit",/^(?:true|false|null|this)\b/],["lit",/^(?:0(?:[0-7]+|x[\da-f]+)l?|(?:0|[1-9]\d*)(?:(?:\.\d+)?(?:e[+-]?\d+)?f?|l?)|\\.\d+(?:e[+-]?\d+)?f?)/i],["typ",/^[$_]*[A-Z][\d$A-Z_]*[a-z][\w$]*/],["pln",/^[$A-Z_a-z][\w$]*/],["com",/^\/(?:\/.*|\*(?:\/|\**[^*/])*(?:\*+\/?)?)/],["pun",/^(?:\.+|\/)/]]),["scala"]);
@@ -1,2 +1,2 @@
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:[^\"\\]|\\.)*"|'(?:[^\'\\]|\\.)*')/,null,"\"'"]],[["com",/^(?:--[^\r\n]*|\/\*[\s\S]*?(?:\*\/|$))/],["kwd",/^(?:ADD|ALL|ALTER|AND|ANY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BULK|BY|CASCADE|CASE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COMMIT|COMPUTE|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DELETE|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DROP|DUMMY|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXEC|EXECUTE|EXISTS|EXIT|FETCH|FILE|FILLFACTOR|FOR|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IN|INDEX|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KILL|LEFT|LIKE|LINENO|LOAD|NATIONAL|NOCHECK|NONCLUSTERED|NOT|NULL|NULLIF|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTION|OR|ORDER|OUTER|OVER|PERCENT|PLAN|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVOKE|RIGHT|ROLLBACK|ROWCOUNT|ROWGUIDCOL|RULE|SAVE|SCHEMA|SELECT|SESSION_USER|SET|SETUSER|SHUTDOWN|SOME|STATISTICS|SYSTEM_USER|TABLE|TEXTSIZE|THEN|TO|TOP|TRAN|TRANSACTION|TRIGGER|TRUNCATE|TSEQUAL|UNION|UNIQUE|UPDATE|UPDATETEXT|USE|USER|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHERE|WHILE|WITH|WRITETEXT)(?=[^\w-]|$)/i,
-null],["lit",/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],["pln",/^[a-z_][\w-]*/i],["pun",/^[^\w\t\n\r \xA0\"\'][^\w\t\n\r \xA0+\-\"\']*/]]),["sql"])
\ No newline at end of file
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r Â\xa0"],["str",/^(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/,null,"\"'"]],[["com",/^(?:--[^\n\r]*|\/\*[\S\s]*?(?:\*\/|$))/],["kwd",/^(?:add|all|alter|and|any|as|asc|authorization|backup|begin|between|break|browse|bulk|by|cascade|case|check|checkpoint|close|clustered|coalesce|collate|column|commit|compute|constraint|contains|containstable|continue|convert|create|cross|current|current_date|current_time|current_timestamp|current_user|cursor|database|dbcc|deallocate|declare|default|delete|deny|desc|disk|distinct|distributed|double|drop|dummy|dump|else|end|errlvl|escape|except|exec|execute|exists|exit|fetch|file|fillfactor|for|foreign|freetext|freetexttable|from|full|function|goto|grant|group|having|holdlock|identity|identitycol|identity_insert|if|in|index|inner|insert|intersect|into|is|join|key|kill|left|like|lineno|load|match|merge|national|nocheck|nonclustered|not|null|nullif|of|off|offsets|on|open|opendatasource|openquery|openrowset|openxml|option|or|order|outer|over|percent|plan|precision|primary|print|proc|procedure|public|raiserror|read|readtext|reconfigure|references|replication|restore|restrict|return|revoke|right|rollback|rowcount|rowguidcol|rule|save|schema|select|session_user|set|setuser|shutdown|some|statistics|system_user|table|textsize|then|to|top|tran|transaction|trigger|truncate|tsequal|union|unique|update|updatetext|use|user|using|values|varying|view|waitfor|when|where|while|with|writetext)(?=[^\w-]|$)/i,
+null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],["pln",/^[_a-z][\w-]*/i],["pun",/^[^\w\t\n\r "'\xa0][^\w\t\n\r "'+\xa0-]*/]]),["sql"]);
@@ -0,0 +1 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r Â\xa0"],["com",/^%[^\n\r]*/,null,"%"]],[["kwd",/^\\[@-Za-z]+/],["kwd",/^\\./],["typ",/^[$&]/],["lit",/[+-]?(?:\.\d+|\d+(?:\.\d*)?)(cm|em|ex|in|pc|pt|bp|mm)/i],["pun",/^[()=[\]{}]+/]]),["latex","tex"]);
@@ -1,2 +1,2 @@
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0\u2028\u2029]+/,null,"\t\n\r \u00a0\u2028\u2029"],["str",/^(?:[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})(?:[\"\u201C\u201D]c|$)|[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})*(?:[\"\u201C\u201D]|$))/i,null,'"\u201c\u201d'],["com",/^[\'\u2018\u2019][^\r\n\u2028\u2029]*/,null,"'\u2018\u2019"]],[["kwd",/^(?:AddHandler|AddressOf|Alias|And|AndAlso|Ansi|As|Assembly|Auto|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|CBool|CByte|CChar|CDate|CDbl|CDec|Char|CInt|Class|CLng|CObj|Const|CShort|CSng|CStr|CType|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else|ElseIf|End|EndIf|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get|GetType|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|Let|Lib|Like|Long|Loop|Me|Mod|Module|MustInherit|MustOverride|MyBase|MyClass|Namespace|New|Next|Not|NotInheritable|NotOverridable|Object|On|Option|Optional|Or|OrElse|Overloads|Overridable|Overrides|ParamArray|Preserve|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|Select|Set|Shadows|Shared|Short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TypeOf|Unicode|Until|Variant|Wend|When|While|With|WithEvents|WriteOnly|Xor|EndIf|GoSub|Let|Variant|Wend)\b/i,
-null],["com",/^REM[^\r\n\u2028\u2029]*/i],["lit",/^(?:True\b|False\b|Nothing\b|\d+(?:E[+\-]?\d+[FRD]?|[FRDSIL])?|(?:&H[0-9A-F]+|&O[0-7]+)[SIL]?|\d*\.\d+(?:E[+\-]?\d+)?[FRD]?|#\s+(?:\d+[\-\/]\d+[\-\/]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)?|\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)\s+#)/i],["pln",/^(?:(?:[a-z]|_\w)\w*|\[(?:[a-z]|_\w)\w*\])/i],["pun",/^[^\w\t\n\r \"\'\[\]\xA0\u2018\u2019\u201C\u201D\u2028\u2029]+/],["pun",/^(?:\[|\])/]]),["vb","vbs"])
\ No newline at end of file
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0\u2028\u2029]+/,null,"\t\n\r Â\xa0

"],["str",/^(?:["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})(?:["\u201c\u201d]c|$)|["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})*(?:["\u201c\u201d]|$))/i,null,'"“”'],["com",/^['\u2018\u2019].*/,null,"'‘’"]],[["kwd",/^(?:addhandler|addressof|alias|and|andalso|ansi|as|assembly|auto|boolean|byref|byte|byval|call|case|catch|cbool|cbyte|cchar|cdate|cdbl|cdec|char|cint|class|clng|cobj|const|cshort|csng|cstr|ctype|date|decimal|declare|default|delegate|dim|directcast|do|double|each|else|elseif|end|endif|enum|erase|error|event|exit|finally|for|friend|function|get|gettype|gosub|goto|handles|if|implements|imports|in|inherits|integer|interface|is|let|lib|like|long|loop|me|mod|module|mustinherit|mustoverride|mybase|myclass|namespace|new|next|not|notinheritable|notoverridable|object|on|option|optional|or|orelse|overloads|overridable|overrides|paramarray|preserve|private|property|protected|public|raiseevent|readonly|redim|removehandler|resume|return|select|set|shadows|shared|short|single|static|step|stop|string|structure|sub|synclock|then|throw|to|try|typeof|unicode|until|variant|wend|when|while|with|withevents|writeonly|xor|endif|gosub|let|variant|wend)\b/i,
+null],["com",/^rem.*/i],["lit",/^(?:true\b|false\b|nothing\b|\d+(?:e[+-]?\d+[dfr]?|[dfilrs])?|(?:&h[\da-f]+|&o[0-7]+)[ils]?|\d*\.\d+(?:e[+-]?\d+)?[dfr]?|#\s+(?:\d+[/-]\d+[/-]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:am|pm))?)?|\d+:\d+(?::\d+)?(\s*(?:am|pm))?)\s+#)/i],["pln",/^(?:(?:[a-z]|_\w)\w*|\[(?:[a-z]|_\w)\w*])/i],["pun",/^[^\w\t\n\r "'[\]\xa0\u2018\u2019\u201c\u201d\u2028\u2029]+/],["pun",/^(?:\[|])/]]),["vb","vbs"]);
@@ -1,3 +1,3 @@
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"]],[["str",/^(?:[BOX]?"(?:[^\"]|"")*"|'.')/i],["com",/^--[^\r\n]*/],["kwd",/^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\w-]|$)/i,
-null],["typ",/^(?:bit|bit_vector|character|boolean|integer|real|time|string|severity_level|positive|natural|signed|unsigned|line|text|std_u?logic(?:_vector)?)(?=[^\w-]|$)/i,null],["typ",/^\'(?:ACTIVE|ASCENDING|BASE|DELAYED|DRIVING|DRIVING_VALUE|EVENT|HIGH|IMAGE|INSTANCE_NAME|LAST_ACTIVE|LAST_EVENT|LAST_VALUE|LEFT|LEFTOF|LENGTH|LOW|PATH_NAME|POS|PRED|QUIET|RANGE|REVERSE_RANGE|RIGHT|RIGHTOF|SIMPLE_NAME|STABLE|SUCC|TRANSACTION|VAL|VALUE)(?=[^\w-]|$)/i,null],["lit",/^\d+(?:_\d+)*(?:#[\w\\.]+#(?:[+\-]?\d+(?:_\d+)*)?|(?:\.\d+(?:_\d+)*)?(?:E[+\-]?\d+(?:_\d+)*)?)/i],
-["pln",/^(?:[a-z]\w*|\\[^\\]*\\)/i],["pun",/^[^\w\t\n\r \xA0\"\'][^\w\t\n\r \xA0\-\"\']*/]]),["vhdl","vhd"])
\ No newline at end of file
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r Â\xa0"]],[["str",/^(?:[box]?"(?:[^"]|"")*"|'.')/i],["com",/^--[^\n\r]*/],["kwd",/^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\w-]|$)/i,
+null],["typ",/^(?:bit|bit_vector|character|boolean|integer|real|time|string|severity_level|positive|natural|signed|unsigned|line|text|std_u?logic(?:_vector)?)(?=[^\w-]|$)/i,null],["typ",/^'(?:active|ascending|base|delayed|driving|driving_value|event|high|image|instance_name|last_active|last_event|last_value|left|leftof|length|low|path_name|pos|pred|quiet|range|reverse_range|right|rightof|simple_name|stable|succ|transaction|val|value)(?=[^\w-]|$)/i,null],["lit",/^\d+(?:_\d+)*(?:#[\w.\\]+#(?:[+-]?\d+(?:_\d+)*)?|(?:\.\d+(?:_\d+)*)?(?:e[+-]?\d+(?:_\d+)*)?)/i],
+["pln",/^(?:[a-z]\w*|\\[^\\]*\\)/i],["pun",/^[^\w\t\n\r "'\xa0][^\w\t\n\r "'\xa0-]*/]]),["vhdl","vhd"]);
@@ -1,2 +1,2 @@
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t \xA0a-gi-z0-9]+/,null,"\t \u00a0abcdefgijklmnopqrstuvwxyz0123456789"],["pun",/^[=*~\^\[\]]+/,null,"=*~^[]"]],[["lang-wiki.meta",/(?:^^|\r\n?|\n)(#[a-z]+)\b/],["lit",/^(?:[A-Z][a-z][a-z0-9]+[A-Z][a-z][a-zA-Z0-9]+)\b/],["lang-",/^\{\{\{([\s\S]+?)\}\}\}/],["lang-",/^`([^\r\n`]+)`/],["str",/^https?:\/\/[^\/?#\s]*(?:\/[^?#\s]*)?(?:\?[^#\s]*)?(?:#\S*)?/i],["pln",/^(?:\r\n|[\s\S])[^#=*~^A-Zh\{`\[\r\n]*/]]),["wiki"]);
-PR.registerLangHandler(PR.createSimpleLexer([["kwd",/^#[a-z]+/i,null,"#"]],[]),["wiki.meta"])
\ No newline at end of file
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\d\t a-gi-z\xa0]+/,null,"\t Â\xa0abcdefgijklmnopqrstuvwxyz0123456789"],["pun",/^[*=[\]^~]+/,null,"=*~^[]"]],[["lang-wiki.meta",/(?:^^|\r\n?|\n)(#[a-z]+)\b/],["lit",/^[A-Z][a-z][\da-z]+[A-Z][a-z][^\W_]+\b/],["lang-",/^{{{([\S\s]+?)}}}/],["lang-",/^`([^\n\r`]+)`/],["str",/^https?:\/\/[^\s#/?]*(?:\/[^\s#?]*)?(?:\?[^\s#]*)?(?:#\S*)?/i],["pln",/^(?:\r\n|[\S\s])[^\n\r#*=A-[^`h{~]*/]]),["wiki"]);
+PR.registerLangHandler(PR.createSimpleLexer([["kwd",/^#[a-z]+/i,null,"#"]],[]),["wiki.meta"]);
@@ -0,0 +1,3 @@
+PR.registerLangHandler(PR.createSimpleLexer([["var pln",/^\$[\w-]+/,null,"$"]],[["pln",/^[\s=][<>][\s=]/],["lit",/^@[\w-]+/],["tag",/^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["com",/^\(:[\S\s]*?:\)/],["pln",/^[(),/;[\]{}]$/],["str",/^(?:"(?:[^"\\{]|\\[\S\s])*(?:"|$)|'(?:[^'\\{]|\\[\S\s])*(?:'|$))/,null,"\"'"],["kwd",/^(?:xquery|where|version|variable|union|typeswitch|treat|to|then|text|stable|sortby|some|self|schema|satisfies|returns|return|ref|processing-instruction|preceding-sibling|preceding|precedes|parent|only|of|node|namespace|module|let|item|intersect|instance|in|import|if|function|for|follows|following-sibling|following|external|except|every|else|element|descending|descendant-or-self|descendant|define|default|declare|comment|child|cast|case|before|attribute|assert|ascending|as|ancestor-or-self|ancestor|after|eq|order|by|or|and|schema-element|document-node|node|at)\b/],
+["typ",/^(?:xs:yearMonthDuration|xs:unsignedLong|xs:time|xs:string|xs:short|xs:QName|xs:Name|xs:long|xs:integer|xs:int|xs:gYearMonth|xs:gYear|xs:gMonthDay|xs:gDay|xs:float|xs:duration|xs:double|xs:decimal|xs:dayTimeDuration|xs:dateTime|xs:date|xs:byte|xs:boolean|xs:anyURI|xf:yearMonthDuration)\b/,null],["fun pln",/^(?:xp:dereference|xinc:node-expand|xinc:link-references|xinc:link-expand|xhtml:restructure|xhtml:clean|xhtml:add-lists|xdmp:zip-manifest|xdmp:zip-get|xdmp:zip-create|xdmp:xquery-version|xdmp:word-convert|xdmp:with-namespaces|xdmp:version|xdmp:value|xdmp:user-roles|xdmp:user-last-login|xdmp:user|xdmp:url-encode|xdmp:url-decode|xdmp:uri-is-file|xdmp:uri-format|xdmp:uri-content-type|xdmp:unquote|xdmp:unpath|xdmp:triggers-database|xdmp:trace|xdmp:to-json|xdmp:tidy|xdmp:subbinary|xdmp:strftime|xdmp:spawn-in|xdmp:spawn|xdmp:sleep|xdmp:shutdown|xdmp:set-session-field|xdmp:set-response-encoding|xdmp:set-response-content-type|xdmp:set-response-code|xdmp:set-request-time-limit|xdmp:set|xdmp:servers|xdmp:server-status|xdmp:server-name|xdmp:server|xdmp:security-database|xdmp:security-assert|xdmp:schema-database|xdmp:save|xdmp:role-roles|xdmp:role|xdmp:rethrow|xdmp:restart|xdmp:request-timestamp|xdmp:request-status|xdmp:request-cancel|xdmp:request|xdmp:redirect-response|xdmp:random|xdmp:quote|xdmp:query-trace|xdmp:query-meters|xdmp:product-edition|xdmp:privilege-roles|xdmp:privilege|xdmp:pretty-print|xdmp:powerpoint-convert|xdmp:platform|xdmp:permission|xdmp:pdf-convert|xdmp:path|xdmp:octal-to-integer|xdmp:node-uri|xdmp:node-replace|xdmp:node-kind|xdmp:node-insert-child|xdmp:node-insert-before|xdmp:node-insert-after|xdmp:node-delete|xdmp:node-database|xdmp:mul64|xdmp:modules-root|xdmp:modules-database|xdmp:merging|xdmp:merge-cancel|xdmp:merge|xdmp:md5|xdmp:logout|xdmp:login|xdmp:log-level|xdmp:log|xdmp:lock-release|xdmp:lock-acquire|xdmp:load|xdmp:invoke-in|xdmp:invoke|xdmp:integer-to-octal|xdmp:integer-to-hex|xdmp:http-put|xdmp:http-post|xdmp:http-options|xdmp:http-head|xdmp:http-get|xdmp:http-delete|xdmp:hosts|xdmp:host-status|xdmp:host-name|xdmp:host|xdmp:hex-to-integer|xdmp:hash64|xdmp:hash32|xdmp:has-privilege|xdmp:groups|xdmp:group-serves|xdmp:group-servers|xdmp:group-name|xdmp:group-hosts|xdmp:group|xdmp:get-session-field-names|xdmp:get-session-field|xdmp:get-response-encoding|xdmp:get-response-code|xdmp:get-request-username|xdmp:get-request-user|xdmp:get-request-url|xdmp:get-request-protocol|xdmp:get-request-path|xdmp:get-request-method|xdmp:get-request-header-names|xdmp:get-request-header|xdmp:get-request-field-names|xdmp:get-request-field-filename|xdmp:get-request-field-content-type|xdmp:get-request-field|xdmp:get-request-client-certificate|xdmp:get-request-client-address|xdmp:get-request-body|xdmp:get-current-user|xdmp:get-current-roles|xdmp:get|xdmp:function-name|xdmp:function-module|xdmp:function|xdmp:from-json|xdmp:forests|xdmp:forest-status|xdmp:forest-restore|xdmp:forest-restart|xdmp:forest-name|xdmp:forest-delete|xdmp:forest-databases|xdmp:forest-counts|xdmp:forest-clear|xdmp:forest-backup|xdmp:forest|xdmp:filesystem-file|xdmp:filesystem-directory|xdmp:exists|xdmp:excel-convert|xdmp:eval-in|xdmp:eval|xdmp:estimate|xdmp:email|xdmp:element-content-type|xdmp:elapsed-time|xdmp:document-set-quality|xdmp:document-set-property|xdmp:document-set-properties|xdmp:document-set-permissions|xdmp:document-set-collections|xdmp:document-remove-properties|xdmp:document-remove-permissions|xdmp:document-remove-collections|xdmp:document-properties|xdmp:document-locks|xdmp:document-load|xdmp:document-insert|xdmp:document-get-quality|xdmp:document-get-properties|xdmp:document-get-permissions|xdmp:document-get-collections|xdmp:document-get|xdmp:document-forest|xdmp:document-delete|xdmp:document-add-properties|xdmp:document-add-permissions|xdmp:document-add-collections|xdmp:directory-properties|xdmp:directory-locks|xdmp:directory-delete|xdmp:directory-create|xdmp:directory|xdmp:diacritic-less|xdmp:describe|xdmp:default-permissions|xdmp:default-collections|xdmp:databases|xdmp:database-restore-validate|xdmp:database-restore-status|xdmp:database-restore-cancel|xdmp:database-restore|xdmp:database-name|xdmp:database-forests|xdmp:database-backup-validate|xdmp:database-backup-status|xdmp:database-backup-purge|xdmp:database-backup-cancel|xdmp:database-backup|xdmp:database|xdmp:collection-properties|xdmp:collection-locks|xdmp:collection-delete|xdmp:collation-canonical-uri|xdmp:castable-as|xdmp:can-grant-roles|xdmp:base64-encode|xdmp:base64-decode|xdmp:architecture|xdmp:apply|xdmp:amp-roles|xdmp:amp|xdmp:add64|xdmp:add-response-header|xdmp:access|trgr:trigger-set-recursive|trgr:trigger-set-permissions|trgr:trigger-set-name|trgr:trigger-set-module|trgr:trigger-set-event|trgr:trigger-set-description|trgr:trigger-remove-permissions|trgr:trigger-module|trgr:trigger-get-permissions|trgr:trigger-enable|trgr:trigger-disable|trgr:trigger-database-online-event|trgr:trigger-data-event|trgr:trigger-add-permissions|trgr:remove-trigger|trgr:property-content|trgr:pre-commit|trgr:post-commit|trgr:get-trigger-by-id|trgr:get-trigger|trgr:document-scope|trgr:document-content|trgr:directory-scope|trgr:create-trigger|trgr:collection-scope|trgr:any-property-content|thsr:set-entry|thsr:remove-term|thsr:remove-synonym|thsr:remove-entry|thsr:query-lookup|thsr:lookup|thsr:load|thsr:insert|thsr:expand|thsr:add-synonym|spell:suggest-detailed|spell:suggest|spell:remove-word|spell:make-dictionary|spell:load|spell:levenshtein-distance|spell:is-correct|spell:insert|spell:double-metaphone|spell:add-word|sec:users-collection|sec:user-set-roles|sec:user-set-password|sec:user-set-name|sec:user-set-description|sec:user-set-default-permissions|sec:user-set-default-collections|sec:user-remove-roles|sec:user-privileges|sec:user-get-roles|sec:user-get-description|sec:user-get-default-permissions|sec:user-get-default-collections|sec:user-doc-permissions|sec:user-doc-collections|sec:user-add-roles|sec:unprotect-collection|sec:uid-for-name|sec:set-realm|sec:security-version|sec:security-namespace|sec:security-installed|sec:security-collection|sec:roles-collection|sec:role-set-roles|sec:role-set-name|sec:role-set-description|sec:role-set-default-permissions|sec:role-set-default-collections|sec:role-remove-roles|sec:role-privileges|sec:role-get-roles|sec:role-get-description|sec:role-get-default-permissions|sec:role-get-default-collections|sec:role-doc-permissions|sec:role-doc-collections|sec:role-add-roles|sec:remove-user|sec:remove-role-from-users|sec:remove-role-from-role|sec:remove-role-from-privileges|sec:remove-role-from-amps|sec:remove-role|sec:remove-privilege|sec:remove-amp|sec:protect-collection|sec:privileges-collection|sec:privilege-set-roles|sec:privilege-set-name|sec:privilege-remove-roles|sec:privilege-get-roles|sec:privilege-add-roles|sec:priv-doc-permissions|sec:priv-doc-collections|sec:get-user-names|sec:get-unique-elem-id|sec:get-role-names|sec:get-role-ids|sec:get-privilege|sec:get-distinct-permissions|sec:get-collection|sec:get-amp|sec:create-user-with-role|sec:create-user|sec:create-role|sec:create-privilege|sec:create-amp|sec:collections-collection|sec:collection-set-permissions|sec:collection-remove-permissions|sec:collection-get-permissions|sec:collection-add-permissions|sec:check-admin|sec:amps-collection|sec:amp-set-roles|sec:amp-remove-roles|sec:amp-get-roles|sec:amp-doc-permissions|sec:amp-doc-collections|sec:amp-add-roles|search:unparse|search:suggest|search:snippet|search:search|search:resolve-nodes|search:resolve|search:remove-constraint|search:parse|search:get-default-options|search:estimate|search:check-options|prof:value|prof:reset|prof:report|prof:invoke|prof:eval|prof:enable|prof:disable|prof:allowed|ppt:clean|pki:template-set-request|pki:template-set-name|pki:template-set-key-type|pki:template-set-key-options|pki:template-set-description|pki:template-in-use|pki:template-get-version|pki:template-get-request|pki:template-get-name|pki:template-get-key-type|pki:template-get-key-options|pki:template-get-id|pki:template-get-description|pki:need-certificate|pki:is-temporary|pki:insert-trusted-certificates|pki:insert-template|pki:insert-signed-certificates|pki:insert-certificate-revocation-list|pki:get-trusted-certificate-ids|pki:get-template-ids|pki:get-template-certificate-authority|pki:get-template-by-name|pki:get-template|pki:get-pending-certificate-requests-xml|pki:get-pending-certificate-requests-pem|pki:get-pending-certificate-request|pki:get-certificates-for-template-xml|pki:get-certificates-for-template|pki:get-certificates|pki:get-certificate-xml|pki:get-certificate-pem|pki:get-certificate|pki:generate-temporary-certificate-if-necessary|pki:generate-temporary-certificate|pki:generate-template-certificate-authority|pki:generate-certificate-request|pki:delete-template|pki:delete-certificate|pki:create-template|pdf:make-toc|pdf:insert-toc-headers|pdf:get-toc|pdf:clean|p:status-transition|p:state-transition|p:remove|p:pipelines|p:insert|p:get-by-id|p:get|p:execute|p:create|p:condition|p:collection|p:action|ooxml:runs-merge|ooxml:package-uris|ooxml:package-parts-insert|ooxml:package-parts|msword:clean|mcgm:polygon|mcgm:point|mcgm:geospatial-query-from-elements|mcgm:geospatial-query|mcgm:circle|math:tanh|math:tan|math:sqrt|math:sinh|math:sin|math:pow|math:modf|math:log10|math:log|math:ldexp|math:frexp|math:fmod|math:floor|math:fabs|math:exp|math:cosh|math:cos|math:ceil|math:atan2|math:atan|math:asin|math:acos|map:put|map:map|map:keys|map:get|map:delete|map:count|map:clear|lnk:to|lnk:remove|lnk:insert|lnk:get|lnk:from|lnk:create|kml:polygon|kml:point|kml:interior-polygon|kml:geospatial-query-from-elements|kml:geospatial-query|kml:circle|kml:box|gml:polygon|gml:point|gml:interior-polygon|gml:geospatial-query-from-elements|gml:geospatial-query|gml:circle|gml:box|georss:point|georss:geospatial-query|georss:circle|geo:polygon|geo:point|geo:interior-polygon|geo:geospatial-query-from-elements|geo:geospatial-query|geo:circle|geo:box|fn:zero-or-one|fn:years-from-duration|fn:year-from-dateTime|fn:year-from-date|fn:upper-case|fn:unordered|fn:true|fn:translate|fn:trace|fn:tokenize|fn:timezone-from-time|fn:timezone-from-dateTime|fn:timezone-from-date|fn:sum|fn:subtract-dateTimes-yielding-yearMonthDuration|fn:subtract-dateTimes-yielding-dayTimeDuration|fn:substring-before|fn:substring-after|fn:substring|fn:subsequence|fn:string-to-codepoints|fn:string-pad|fn:string-length|fn:string-join|fn:string|fn:static-base-uri|fn:starts-with|fn:seconds-from-time|fn:seconds-from-duration|fn:seconds-from-dateTime|fn:round-half-to-even|fn:round|fn:root|fn:reverse|fn:resolve-uri|fn:resolve-QName|fn:replace|fn:remove|fn:QName|fn:prefix-from-QName|fn:position|fn:one-or-more|fn:number|fn:not|fn:normalize-unicode|fn:normalize-space|fn:node-name|fn:node-kind|fn:nilled|fn:namespace-uri-from-QName|fn:namespace-uri-for-prefix|fn:namespace-uri|fn:name|fn:months-from-duration|fn:month-from-dateTime|fn:month-from-date|fn:minutes-from-time|fn:minutes-from-duration|fn:minutes-from-dateTime|fn:min|fn:max|fn:matches|fn:lower-case|fn:local-name-from-QName|fn:local-name|fn:last|fn:lang|fn:iri-to-uri|fn:insert-before|fn:index-of|fn:in-scope-prefixes|fn:implicit-timezone|fn:idref|fn:id|fn:hours-from-time|fn:hours-from-duration|fn:hours-from-dateTime|fn:floor|fn:false|fn:expanded-QName|fn:exists|fn:exactly-one|fn:escape-uri|fn:escape-html-uri|fn:error|fn:ends-with|fn:encode-for-uri|fn:empty|fn:document-uri|fn:doc-available|fn:doc|fn:distinct-values|fn:distinct-nodes|fn:default-collation|fn:deep-equal|fn:days-from-duration|fn:day-from-dateTime|fn:day-from-date|fn:data|fn:current-time|fn:current-dateTime|fn:current-date|fn:count|fn:contains|fn:concat|fn:compare|fn:collection|fn:codepoints-to-string|fn:codepoint-equal|fn:ceiling|fn:boolean|fn:base-uri|fn:avg|fn:adjust-time-to-timezone|fn:adjust-dateTime-to-timezone|fn:adjust-date-to-timezone|fn:abs|feed:unsubscribe|feed:subscription|feed:subscribe|feed:request|feed:item|feed:description|excel:clean|entity:enrich|dom:set-pipelines|dom:set-permissions|dom:set-name|dom:set-evaluation-context|dom:set-domain-scope|dom:set-description|dom:remove-pipeline|dom:remove-permissions|dom:remove|dom:get|dom:evaluation-context|dom:domains|dom:domain-scope|dom:create|dom:configuration-set-restart-user|dom:configuration-set-permissions|dom:configuration-set-evaluation-context|dom:configuration-set-default-domain|dom:configuration-get|dom:configuration-create|dom:collection|dom:add-pipeline|dom:add-permissions|dls:retention-rules|dls:retention-rule-remove|dls:retention-rule-insert|dls:retention-rule|dls:purge|dls:node-expand|dls:link-references|dls:link-expand|dls:documents-query|dls:document-versions-query|dls:document-version-uri|dls:document-version-query|dls:document-version-delete|dls:document-version-as-of|dls:document-version|dls:document-update|dls:document-unmanage|dls:document-set-quality|dls:document-set-property|dls:document-set-properties|dls:document-set-permissions|dls:document-set-collections|dls:document-retention-rules|dls:document-remove-properties|dls:document-remove-permissions|dls:document-remove-collections|dls:document-purge|dls:document-manage|dls:document-is-managed|dls:document-insert-and-manage|dls:document-include-query|dls:document-history|dls:document-get-permissions|dls:document-extract-part|dls:document-delete|dls:document-checkout-status|dls:document-checkout|dls:document-checkin|dls:document-add-properties|dls:document-add-permissions|dls:document-add-collections|dls:break-checkout|dls:author-query|dls:as-of-query|dbk:convert|dbg:wait|dbg:value|dbg:stopped|dbg:stop|dbg:step|dbg:status|dbg:stack|dbg:out|dbg:next|dbg:line|dbg:invoke|dbg:function|dbg:finish|dbg:expr|dbg:eval|dbg:disconnect|dbg:detach|dbg:continue|dbg:connect|dbg:clear|dbg:breakpoints|dbg:break|dbg:attached|dbg:attach|cvt:save-converted-documents|cvt:part-uri|cvt:destination-uri|cvt:basepath|cvt:basename|cts:words|cts:word-query-weight|cts:word-query-text|cts:word-query-options|cts:word-query|cts:word-match|cts:walk|cts:uris|cts:uri-match|cts:train|cts:tokenize|cts:thresholds|cts:stem|cts:similar-query-weight|cts:similar-query-nodes|cts:similar-query|cts:shortest-distance|cts:search|cts:score|cts:reverse-query-weight|cts:reverse-query-nodes|cts:reverse-query|cts:remainder|cts:registered-query-weight|cts:registered-query-options|cts:registered-query-ids|cts:registered-query|cts:register|cts:query|cts:quality|cts:properties-query-query|cts:properties-query|cts:polygon-vertices|cts:polygon|cts:point-longitude|cts:point-latitude|cts:point|cts:or-query-queries|cts:or-query|cts:not-query-weight|cts:not-query-query|cts:not-query|cts:near-query-weight|cts:near-query-queries|cts:near-query-options|cts:near-query-distance|cts:near-query|cts:highlight|cts:geospatial-co-occurrences|cts:frequency|cts:fitness|cts:field-words|cts:field-word-query-weight|cts:field-word-query-text|cts:field-word-query-options|cts:field-word-query-field-name|cts:field-word-query|cts:field-word-match|cts:entity-highlight|cts:element-words|cts:element-word-query-weight|cts:element-word-query-text|cts:element-word-query-options|cts:element-word-query-element-name|cts:element-word-query|cts:element-word-match|cts:element-values|cts:element-value-ranges|cts:element-value-query-weight|cts:element-value-query-text|cts:element-value-query-options|cts:element-value-query-element-name|cts:element-value-query|cts:element-value-match|cts:element-value-geospatial-co-occurrences|cts:element-value-co-occurrences|cts:element-range-query-weight|cts:element-range-query-value|cts:element-range-query-options|cts:element-range-query-operator|cts:element-range-query-element-name|cts:element-range-query|cts:element-query-query|cts:element-query-element-name|cts:element-query|cts:element-pair-geospatial-values|cts:element-pair-geospatial-value-match|cts:element-pair-geospatial-query-weight|cts:element-pair-geospatial-query-region|cts:element-pair-geospatial-query-options|cts:element-pair-geospatial-query-longitude-name|cts:element-pair-geospatial-query-latitude-name|cts:element-pair-geospatial-query-element-name|cts:element-pair-geospatial-query|cts:element-pair-geospatial-boxes|cts:element-geospatial-values|cts:element-geospatial-value-match|cts:element-geospatial-query-weight|cts:element-geospatial-query-region|cts:element-geospatial-query-options|cts:element-geospatial-query-element-name|cts:element-geospatial-query|cts:element-geospatial-boxes|cts:element-child-geospatial-values|cts:element-child-geospatial-value-match|cts:element-child-geospatial-query-weight|cts:element-child-geospatial-query-region|cts:element-child-geospatial-query-options|cts:element-child-geospatial-query-element-name|cts:element-child-geospatial-query-child-name|cts:element-child-geospatial-query|cts:element-child-geospatial-boxes|cts:element-attribute-words|cts:element-attribute-word-query-weight|cts:element-attribute-word-query-text|cts:element-attribute-word-query-options|cts:element-attribute-word-query-element-name|cts:element-attribute-word-query-attribute-name|cts:element-attribute-word-query|cts:element-attribute-word-match|cts:element-attribute-values|cts:element-attribute-value-ranges|cts:element-attribute-value-query-weight|cts:element-attribute-value-query-text|cts:element-attribute-value-query-options|cts:element-attribute-value-query-element-name|cts:element-attribute-value-query-attribute-name|cts:element-attribute-value-query|cts:element-attribute-value-match|cts:element-attribute-value-geospatial-co-occurrences|cts:element-attribute-value-co-occurrences|cts:element-attribute-range-query-weight|cts:element-attribute-range-query-value|cts:element-attribute-range-query-options|cts:element-attribute-range-query-operator|cts:element-attribute-range-query-element-name|cts:element-attribute-range-query-attribute-name|cts:element-attribute-range-query|cts:element-attribute-pair-geospatial-values|cts:element-attribute-pair-geospatial-value-match|cts:element-attribute-pair-geospatial-query-weight|cts:element-attribute-pair-geospatial-query-region|cts:element-attribute-pair-geospatial-query-options|cts:element-attribute-pair-geospatial-query-longitude-name|cts:element-attribute-pair-geospatial-query-latitude-name|cts:element-attribute-pair-geospatial-query-element-name|cts:element-attribute-pair-geospatial-query|cts:element-attribute-pair-geospatial-boxes|cts:document-query-uris|cts:document-query|cts:distance|cts:directory-query-uris|cts:directory-query-depth|cts:directory-query|cts:destination|cts:deregister|cts:contains|cts:confidence|cts:collections|cts:collection-query-uris|cts:collection-query|cts:collection-match|cts:classify|cts:circle-radius|cts:circle-center|cts:circle|cts:box-west|cts:box-south|cts:box-north|cts:box-east|cts:box|cts:bearing|cts:arc-intersection|cts:and-query-queries|cts:and-query-options|cts:and-query|cts:and-not-query-positive-query|cts:and-not-query-negative-query|cts:and-not-query|css:get|css:convert|cpf:success|cpf:failure|cpf:document-set-state|cpf:document-set-processing-status|cpf:document-set-last-updated|cpf:document-set-error|cpf:document-get-state|cpf:document-get-processing-status|cpf:document-get-last-updated|cpf:document-get-error|cpf:check-transition|alert:spawn-matching-actions|alert:rule-user-id-query|alert:rule-set-user-id|alert:rule-set-query|alert:rule-set-options|alert:rule-set-name|alert:rule-set-description|alert:rule-set-action|alert:rule-remove|alert:rule-name-query|alert:rule-insert|alert:rule-id-query|alert:rule-get-user-id|alert:rule-get-query|alert:rule-get-options|alert:rule-get-name|alert:rule-get-id|alert:rule-get-description|alert:rule-get-action|alert:rule-action-query|alert:remove-triggers|alert:make-rule|alert:make-log-action|alert:make-config|alert:make-action|alert:invoke-matching-actions|alert:get-my-rules|alert:get-all-rules|alert:get-actions|alert:find-matching-rules|alert:create-triggers|alert:config-set-uri|alert:config-set-trigger-ids|alert:config-set-options|alert:config-set-name|alert:config-set-description|alert:config-set-cpf-domain-names|alert:config-set-cpf-domain-ids|alert:config-insert|alert:config-get-uri|alert:config-get-trigger-ids|alert:config-get-options|alert:config-get-name|alert:config-get-id|alert:config-get-description|alert:config-get-cpf-domain-names|alert:config-get-cpf-domain-ids|alert:config-get|alert:config-delete|alert:action-set-options|alert:action-set-name|alert:action-set-module-root|alert:action-set-module-db|alert:action-set-module|alert:action-set-description|alert:action-remove|alert:action-insert|alert:action-get-options|alert:action-get-name|alert:action-get-module-root|alert:action-get-module-db|alert:action-get-module|alert:action-get-description|zero-or-one|years-from-duration|year-from-dateTime|year-from-date|upper-case|unordered|true|translate|trace|tokenize|timezone-from-time|timezone-from-dateTime|timezone-from-date|sum|subtract-dateTimes-yielding-yearMonthDuration|subtract-dateTimes-yielding-dayTimeDuration|substring-before|substring-after|substring|subsequence|string-to-codepoints|string-pad|string-length|string-join|string|static-base-uri|starts-with|seconds-from-time|seconds-from-duration|seconds-from-dateTime|round-half-to-even|round|root|reverse|resolve-uri|resolve-QName|replace|remove|QName|prefix-from-QName|position|one-or-more|number|not|normalize-unicode|normalize-space|node-name|node-kind|nilled|namespace-uri-from-QName|namespace-uri-for-prefix|namespace-uri|name|months-from-duration|month-from-dateTime|month-from-date|minutes-from-time|minutes-from-duration|minutes-from-dateTime|min|max|matches|lower-case|local-name-from-QName|local-name|last|lang|iri-to-uri|insert-before|index-of|in-scope-prefixes|implicit-timezone|idref|id|hours-from-time|hours-from-duration|hours-from-dateTime|floor|false|expanded-QName|exists|exactly-one|escape-uri|escape-html-uri|error|ends-with|encode-for-uri|empty|document-uri|doc-available|doc|distinct-values|distinct-nodes|default-collation|deep-equal|days-from-duration|day-from-dateTime|day-from-date|data|current-time|current-dateTime|current-date|count|contains|concat|compare|collection|codepoints-to-string|codepoint-equal|ceiling|boolean|base-uri|avg|adjust-time-to-timezone|adjust-dateTime-to-timezone|adjust-date-to-timezone|abs)\b/],
+["pln",/^[\w:-]+/],["pln",/^[\t\n\r \xa0]+/]]),["xq","xquery"]);
@@ -1,2 +1,2 @@
-PR.registerLangHandler(PR.createSimpleLexer([["pun",/^[:|>?]+/,null,":|>?"],["dec",/^%(?:YAML|TAG)[^#\r\n]+/,null,"%"],["typ",/^[&]\S+/,null,"&"],["typ",/^!\S*/,null,"!"],["str",/^"(?:[^\\"]|\\.)*(?:"|$)/,null,'"'],["str",/^'(?:[^']|'')*(?:'|$)/,null,"'"],["com",/^#[^\r\n]*/,null,"#"],["pln",/^\s+/,null," \t\r\n"]],[["dec",/^(?:---|\.\.\.)(?:[\r\n]|$)/],["pun",/^-/],["kwd",/^\w+:[ \r\n]/],["pln",/^\w+/]]),
-["yaml","yml"])
\ No newline at end of file
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["pun",/^[:>?|]+/,a,":|>?"],["dec",/^%(?:YAML|TAG)[^\n\r#]+/,a,"%"],["typ",/^&\S+/,a,"&"],["typ",/^!\S*/,a,"!"],["str",/^"(?:[^"\\]|\\.)*(?:"|$)/,a,'"'],["str",/^'(?:[^']|'')*(?:'|$)/,a,"'"],["com",/^#[^\n\r]*/,a,"#"],["pln",/^\s+/,a," \t\r\n"]],[["dec",/^(?:---|\.\.\.)(?:[\n\r]|$)/],["pun",/^-/],["kwd",/^\w+:[\n\r ]/],["pln",/^\w+/]]),["yaml","yml"]);
@@ -1,33 +1,28 @@
-window.PR_SHOULD_USE_CONTINUATION=true;window.PR_TAB_WIDTH=8;window.PR_normalizedHtml=window.PR=window.prettyPrintOne=window.prettyPrint=void 0;window._pr_isIE6=function(){var y=navigator&&navigator.userAgent&&navigator.userAgent.match(/\bMSIE ([678])\./);y=y?+y[1]:false;window._pr_isIE6=function(){return y};return y};
-(function(){function y(b){return b.replace(L,"&amp;").replace(M,"&lt;").replace(N,"&gt;")}function H(b,f,i){switch(b.nodeType){case 1:var o=b.tagName.toLowerCase();f.push("<",o);var l=b.attributes,n=l.length;if(n){if(i){for(var r=[],j=n;--j>=0;)r[j]=l[j];r.sort(function(q,m){return q.name<m.name?-1:q.name===m.name?0:1});l=r}for(j=0;j<n;++j){r=l[j];r.specified&&f.push(" ",r.name.toLowerCase(),'="',r.value.replace(L,"&amp;").replace(M,"&lt;").replace(N,"&gt;").replace(X,"&quot;"),'"')}}f.push(">");
-for(l=b.firstChild;l;l=l.nextSibling)H(l,f,i);if(b.firstChild||!/^(?:br|link|img)$/.test(o))f.push("</",o,">");break;case 3:case 4:f.push(y(b.nodeValue));break}}function O(b){function f(c){if(c.charAt(0)!=="\\")return c.charCodeAt(0);switch(c.charAt(1)){case "b":return 8;case "t":return 9;case "n":return 10;case "v":return 11;case "f":return 12;case "r":return 13;case "u":case "x":return parseInt(c.substring(2),16)||c.charCodeAt(1);case "0":case "1":case "2":case "3":case "4":case "5":case "6":case "7":return parseInt(c.substring(1),
-8);default:return c.charCodeAt(1)}}function i(c){if(c<32)return(c<16?"\\x0":"\\x")+c.toString(16);c=String.fromCharCode(c);if(c==="\\"||c==="-"||c==="["||c==="]")c="\\"+c;return c}function o(c){var d=c.substring(1,c.length-1).match(RegExp("\\\\u[0-9A-Fa-f]{4}|\\\\x[0-9A-Fa-f]{2}|\\\\[0-3][0-7]{0,2}|\\\\[0-7]{1,2}|\\\\[\\s\\S]|-|[^-\\\\]","g"));c=[];for(var a=[],k=d[0]==="^",e=k?1:0,h=d.length;e<h;++e){var g=d[e];switch(g){case "\\B":case "\\b":case "\\D":case "\\d":case "\\S":case "\\s":case "\\W":case "\\w":c.push(g);
-continue}g=f(g);var s;if(e+2<h&&"-"===d[e+1]){s=f(d[e+2]);e+=2}else s=g;a.push([g,s]);if(!(s<65||g>122)){s<65||g>90||a.push([Math.max(65,g)|32,Math.min(s,90)|32]);s<97||g>122||a.push([Math.max(97,g)&-33,Math.min(s,122)&-33])}}a.sort(function(v,w){return v[0]-w[0]||w[1]-v[1]});d=[];g=[NaN,NaN];for(e=0;e<a.length;++e){h=a[e];if(h[0]<=g[1]+1)g[1]=Math.max(g[1],h[1]);else d.push(g=h)}a=["["];k&&a.push("^");a.push.apply(a,c);for(e=0;e<d.length;++e){h=d[e];a.push(i(h[0]));if(h[1]>h[0]){h[1]+1>h[0]&&a.push("-");
-a.push(i(h[1]))}}a.push("]");return a.join("")}function l(c){for(var d=c.source.match(RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g")),a=d.length,k=[],e=0,h=0;e<a;++e){var g=d[e];if(g==="(")++h;else if("\\"===g.charAt(0))if((g=+g.substring(1))&&g<=h)k[g]=-1}for(e=1;e<k.length;++e)if(-1===k[e])k[e]=++n;for(h=e=0;e<a;++e){g=d[e];if(g==="("){++h;if(k[h]===undefined)d[e]="(?:"}else if("\\"===
-g.charAt(0))if((g=+g.substring(1))&&g<=h)d[e]="\\"+k[h]}for(h=e=0;e<a;++e)if("^"===d[e]&&"^"!==d[e+1])d[e]="";if(c.ignoreCase&&r)for(e=0;e<a;++e){g=d[e];c=g.charAt(0);if(g.length>=2&&c==="[")d[e]=o(g);else if(c!=="\\")d[e]=g.replace(/[a-zA-Z]/g,function(s){s=s.charCodeAt(0);return"["+String.fromCharCode(s&-33,s|32)+"]"})}return d.join("")}for(var n=0,r=false,j=false,q=0,m=b.length;q<m;++q){var t=b[q];if(t.ignoreCase)j=true;else if(/[a-z]/i.test(t.source.replace(/\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi,
-""))){r=true;j=false;break}}var p=[];q=0;for(m=b.length;q<m;++q){t=b[q];if(t.global||t.multiline)throw Error(""+t);p.push("(?:"+l(t)+")")}return RegExp(p.join("|"),j?"gi":"g")}function Y(b){var f=0;return function(i){for(var o=null,l=0,n=0,r=i.length;n<r;++n)switch(i.charAt(n)){case "\t":o||(o=[]);o.push(i.substring(l,n));l=b-f%b;for(f+=l;l>=0;l-=16)o.push("                ".substring(0,l));l=n+1;break;case "\n":f=0;break;default:++f}if(!o)return i;o.push(i.substring(l));return o.join("")}}function I(b,
-f,i,o){if(f){b={source:f,c:b};i(b);o.push.apply(o,b.d)}}function B(b,f){var i={},o;(function(){for(var r=b.concat(f),j=[],q={},m=0,t=r.length;m<t;++m){var p=r[m],c=p[3];if(c)for(var d=c.length;--d>=0;)i[c.charAt(d)]=p;p=p[1];c=""+p;if(!q.hasOwnProperty(c)){j.push(p);q[c]=null}}j.push(/[\0-\uffff]/);o=O(j)})();var l=f.length;function n(r){for(var j=r.c,q=[j,z],m=0,t=r.source.match(o)||[],p={},c=0,d=t.length;c<d;++c){var a=t[c],k=p[a],e=void 0,h;if(typeof k==="string")h=false;else{var g=i[a.charAt(0)];
-if(g){e=a.match(g[1]);k=g[0]}else{for(h=0;h<l;++h){g=f[h];if(e=a.match(g[1])){k=g[0];break}}e||(k=z)}if((h=k.length>=5&&"lang-"===k.substring(0,5))&&!(e&&typeof e[1]==="string")){h=false;k=P}h||(p[a]=k)}g=m;m+=a.length;if(h){h=e[1];var s=a.indexOf(h),v=s+h.length;if(e[2]){v=a.length-e[2].length;s=v-h.length}k=k.substring(5);I(j+g,a.substring(0,s),n,q);I(j+g+s,h,Q(k,h),q);I(j+g+v,a.substring(v),n,q)}else q.push(j+g,k)}r.d=q}return n}function x(b){var f=[],i=[];if(b.tripleQuotedStrings)f.push([A,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
-null,"'\""]);else b.multiLineStrings?f.push([A,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"]):f.push([A,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"]);b.verbatimStrings&&i.push([A,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null]);if(b.hashComments)if(b.cStyleComments){f.push([C,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"]);i.push([A,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,
-null])}else f.push([C,/^#[^\r\n]*/,null,"#"]);if(b.cStyleComments){i.push([C,/^\/\/[^\r\n]*/,null]);i.push([C,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}b.regexLiterals&&i.push(["lang-regex",RegExp("^"+Z+"(/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/)")]);b=b.keywords.replace(/^\s+|\s+$/g,"");b.length&&i.push([R,RegExp("^(?:"+b.replace(/\s+/g,"|")+")\\b"),null]);f.push([z,/^\s+/,null," \r\n\t\u00a0"]);i.push([J,/^@[a-z_$][a-z_$@0-9]*/i,null],[S,/^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/,
-null],[z,/^[a-z_$][a-z_$@0-9]*/i,null],[J,/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],[E,/^.[^\s\w\.$@\'\"\`\/\#]*/,null]);return B(f,i)}function $(b){function f(D){if(D>r){if(j&&j!==q){n.push("</span>");j=null}if(!j&&q){j=q;n.push('<span class="',j,'">')}var T=y(p(i.substring(r,D))).replace(e?d:c,"$1&#160;");e=k.test(T);n.push(T.replace(a,s));r=D}}var i=b.source,o=b.g,l=b.d,n=[],r=0,j=null,q=null,m=0,t=0,p=Y(window.PR_TAB_WIDTH),c=/([\r\n ]) /g,
-d=/(^| ) /gm,a=/\r\n?|\n/g,k=/[ \r\n]$/,e=true,h=window._pr_isIE6();h=h?b.b.tagName==="PRE"?h===6?"&#160;\r\n":h===7?"&#160;<br>\r":"&#160;\r":"&#160;<br />":"<br />";var g=b.b.className.match(/\blinenums\b(?::(\d+))?/),s;if(g){for(var v=[],w=0;w<10;++w)v[w]=h+'</li><li class="L'+w+'">';var F=g[1]&&g[1].length?g[1]-1:0;n.push('<ol class="linenums"><li class="L',F%10,'"');F&&n.push(' value="',F+1,'"');n.push(">");s=function(){var D=v[++F%10];return j?"</span>"+D+'<span class="'+j+'">':D}}else s=h;
-for(;;)if(m<o.length?t<l.length?o[m]<=l[t]:true:false){f(o[m]);if(j){n.push("</span>");j=null}n.push(o[m+1]);m+=2}else if(t<l.length){f(l[t]);q=l[t+1];t+=2}else break;f(i.length);j&&n.push("</span>");g&&n.push("</li></ol>");b.a=n.join("")}function u(b,f){for(var i=f.length;--i>=0;){var o=f[i];if(G.hasOwnProperty(o))"console"in window&&console.warn("cannot override language handler %s",o);else G[o]=b}}function Q(b,f){b&&G.hasOwnProperty(b)||(b=/^\s*</.test(f)?"default-markup":"default-code");return G[b]}
-function U(b){var f=b.f,i=b.e;b.a=f;try{var o,l=f.match(aa);f=[];var n=0,r=[];if(l)for(var j=0,q=l.length;j<q;++j){var m=l[j];if(m.length>1&&m.charAt(0)==="<"){if(!ba.test(m))if(ca.test(m)){f.push(m.substring(9,m.length-3));n+=m.length-12}else if(da.test(m)){f.push("\n");++n}else if(m.indexOf(V)>=0&&m.replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,' $1="$2$3$4"').match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/)){var t=m.match(W)[2],p=1,c;c=j+1;a:for(;c<q;++c){var d=l[c].match(W);if(d&&
-d[2]===t)if(d[1]==="/"){if(--p===0)break a}else++p}if(c<q){r.push(n,l.slice(j,c+1).join(""));j=c}else r.push(n,m)}else r.push(n,m)}else{var a;p=m;var k=p.indexOf("&");if(k<0)a=p;else{for(--k;(k=p.indexOf("&#",k+1))>=0;){var e=p.indexOf(";",k);if(e>=0){var h=p.substring(k+3,e),g=10;if(h&&h.charAt(0)==="x"){h=h.substring(1);g=16}var s=parseInt(h,g);isNaN(s)||(p=p.substring(0,k)+String.fromCharCode(s)+p.substring(e+1))}}a=p.replace(ea,"<").replace(fa,">").replace(ga,"'").replace(ha,'"').replace(ia," ").replace(ja,
-"&")}f.push(a);n+=a.length}}o={source:f.join(""),h:r};var v=o.source;b.source=v;b.c=0;b.g=o.h;Q(i,v)(b);$(b)}catch(w){if("console"in window)console.log(w&&w.stack?w.stack:w)}}var A="str",R="kwd",C="com",S="typ",J="lit",E="pun",z="pln",P="src",V="nocode",Z=function(){for(var b=["!","!=","!==","#","%","%=","&","&&","&&=","&=","(","*","*=","+=",",","-=","->","/","/=",":","::",";","<","<<","<<=","<=","=","==","===",">",">=",">>",">>=",">>>",">>>=","?","@","[","^","^=","^^","^^=","{","|","|=","||","||=",
-"~","break","case","continue","delete","do","else","finally","instanceof","return","throw","try","typeof"],f="(?:^^|[+-]",i=0;i<b.length;++i)f+="|"+b[i].replace(/([^=<>:&a-z])/g,"\\$1");f+=")\\s*";return f}(),L=/&/g,M=/</g,N=/>/g,X=/\"/g,ea=/&lt;/g,fa=/&gt;/g,ga=/&apos;/g,ha=/&quot;/g,ja=/&amp;/g,ia=/&nbsp;/g,ka=/[\r\n]/g,K=null,aa=RegExp("[^<]+|<!--[\\s\\S]*?--\>|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>|</?[a-zA-Z](?:[^>\"']|'[^']*'|\"[^\"]*\")*>|<","g"),ba=/^<\!--/,ca=/^<!\[CDATA\[/,da=/^<br\b/i,W=/^<(\/?)([a-zA-Z][a-zA-Z0-9]*)/,
-la=x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof alignof align_union asm axiom bool concept concept_map const_cast constexpr decltype dynamic_cast explicit export friend inline late_check mutable namespace nullptr reinterpret_cast static_assert static_cast template typeid typename using virtual wchar_t where break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof abstract boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient as base by checked decimal delegate descending event fixed foreach from group implicit in interface internal into is lock object out override orderby params partial readonly ref sbyte sealed stackalloc string select uint ulong unchecked unsafe ushort var break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof debugger eval export function get null set undefined var with Infinity NaN caller delete die do dump elsif eval exit foreach for goto if import last local my next no our print package redo require sub undef unless until use wantarray while BEGIN END break continue do else for if return while and as assert class def del elif except exec finally from global import in is lambda nonlocal not or pass print raise try with yield False True None break continue do else for if return while alias and begin case class def defined elsif end ensure false in module next nil not or redo rescue retry self super then true undef unless until when yield BEGIN END break continue do else for if return while case done elif esac eval fi function in local set then until ",
-hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true}),G={};u(la,["default-code"]);u(B([],[[z,/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],[C,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[E,/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup",
-"htm","html","mxml","xhtml","xml","xsl"]);u(B([[z,/^[\s]+/,null," \t\r\n"],["atv",/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[E,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],
-["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);u(B([],[["atv",/^[\s\S]+/]]),["uq.val"]);u(x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof alignof align_union asm axiom bool concept concept_map const_cast constexpr decltype dynamic_cast explicit export friend inline late_check mutable namespace nullptr reinterpret_cast static_assert static_cast template typeid typename using virtual wchar_t where ",
-hashComments:true,cStyleComments:true}),["c","cc","cpp","cxx","cyc","m"]);u(x({keywords:"null true false"}),["json"]);u(x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof abstract boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient as base by checked decimal delegate descending event fixed foreach from group implicit in interface internal into is lock object out override orderby params partial readonly ref sbyte sealed stackalloc string select uint ulong unchecked unsafe ushort var ",
-hashComments:true,cStyleComments:true,verbatimStrings:true}),["cs"]);u(x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof abstract boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient ",
-cStyleComments:true}),["java"]);u(x({keywords:"break continue do else for if return while case done elif esac eval fi function in local set then until ",hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);u(x({keywords:"break continue do else for if return while and as assert class def del elif except exec finally from global import in is lambda nonlocal not or pass print raise try with yield False True None ",hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);
-u(x({keywords:"caller delete die do dump elsif eval exit foreach for goto if import last local my next no our print package redo require sub undef unless until use wantarray while BEGIN END ",hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);u(x({keywords:"break continue do else for if return while alias and begin case class def defined elsif end ensure false in module next nil not or redo rescue retry self super then true undef unless until when yield BEGIN END ",hashComments:true,
-multiLineStrings:true,regexLiterals:true}),["rb"]);u(x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof debugger eval export function get null set undefined var with Infinity NaN ",cStyleComments:true,regexLiterals:true}),["js"]);u(B([],[[A,/^[\s\S]+/]]),
-["regex"]);window.PR_normalizedHtml=H;window.prettyPrintOne=function(b,f){var i={f:b,e:f};U(i);return i.a};window.prettyPrint=function(b){function f(){for(var t=window.PR_SHOULD_USE_CONTINUATION?j.now()+250:Infinity;q<o.length&&j.now()<t;q++){var p=o[q];if(p.className&&p.className.indexOf("prettyprint")>=0){var c=p.className.match(/\blang-(\w+)\b/);if(c)c=c[1];for(var d=false,a=p.parentNode;a;a=a.parentNode)if((a.tagName==="pre"||a.tagName==="code"||a.tagName==="xmp")&&a.className&&a.className.indexOf("prettyprint")>=
-0){d=true;break}if(!d){a=p;if(null===K){d=document.createElement("PRE");d.appendChild(document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));K=!/</.test(d.innerHTML)}if(K){d=a.innerHTML;if("XMP"===a.tagName)d=y(d);else{a=a;if("PRE"===a.tagName)a=true;else if(ka.test(d)){var k="";if(a.currentStyle)k=a.currentStyle.whiteSpace;else if(window.getComputedStyle)k=window.getComputedStyle(a,null).whiteSpace;a=!k||k==="pre"}else a=true;a||(d=d.replace(/(<br\s*\/?>)[\r\n]+/g,"$1").replace(/(?:[\r\n]+[ \t]*)+/g,
-" "))}d=d}else{d=[];for(a=a.firstChild;a;a=a.nextSibling)H(a,d);d=d.join("")}d=d.replace(/(?:\r\n?|\n)$/,"");m={f:d,e:c,b:p};U(m);if(p=m.a){c=m.b;if("XMP"===c.tagName){d=document.createElement("PRE");for(a=0;a<c.attributes.length;++a){k=c.attributes[a];if(k.specified)if(k.name.toLowerCase()==="class")d.className=k.value;else d.setAttribute(k.name,k.value)}d.innerHTML=p;c.parentNode.replaceChild(d,c)}else c.innerHTML=p}}}}if(q<o.length)setTimeout(f,250);else b&&b()}for(var i=[document.getElementsByTagName("pre"),
-document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],o=[],l=0;l<i.length;++l)for(var n=0,r=i[l].length;n<r;++n)o.push(i[l][n]);i=null;var j=Date;j.now||(j={now:function(){return(new Date).getTime()}});var q=0,m;f()};window.PR={combinePrefixPatterns:O,createSimpleLexer:B,registerLangHandler:u,sourceDecorator:x,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:C,PR_DECLARATION:"dec",PR_KEYWORD:R,PR_LITERAL:J,PR_NOCODE:V,PR_PLAIN:z,PR_PUNCTUATION:E,PR_SOURCE:P,PR_STRING:A,
-PR_TAG:"tag",PR_TYPE:S}})()
\ No newline at end of file
+var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
+(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
+[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
+f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
+(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
+{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
+t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
+"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
+l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
+q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
+q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
+"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
+a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
+for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
+m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
+a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
+j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
+"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
+H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
+J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
+I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
+["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
+/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
+["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
+hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
+!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
+250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
+PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();
@@ -112,7 +112,7 @@
       %= tag 'tr', $i ? (class => 'important') : undef, begin
         <td class="key"><%= $key %>.</td>
         <td class="value">
-          %== $code->($value)
+          %= $code->($value)
         </td>
       % end
     % end
@@ -130,13 +130,13 @@
       <div id="context">
         <table>
           % for my $line (@{$e->lines_before}) {
-            %== $cv->($line->[0], $line->[1])
+            %= $cv->($line->[0], $line->[1])
           % }
           % if (defined $e->line->[1]) {
-            %== $cv->($e->line->[0], $e->line->[1], 1)
+            %= $cv->($e->line->[0], $e->line->[1], 1)
           % }
           % for my $line (@{$e->lines_after}) {
-            %== $cv->($line->[0], $line->[1])
+            %= $cv->($line->[0], $line->[1])
           % }
         </table>
       </div>
@@ -144,11 +144,11 @@
         <div id="insight">
           <table>
             % for my $line (@{$e->lines_before}) {
-              %== $cv->($line->[0], $line->[2])
+              %= $cv->($line->[0], $line->[2])
             % }
-            %== $cv->($e->line->[0], $e->line->[2], 1)
+            %= $cv->($e->line->[0], $e->line->[2], 1)
             % for my $line (@{$e->lines_after}) {
-              %== $cv->($line->[0], $line->[2])
+              %= $cv->($line->[0], $line->[2])
             % }
           </table>
         </div>
@@ -178,7 +178,7 @@
               <div class="file"><%= $frame->[1] %></div>
               <div class="code preview">
                 %= "$frame->[2]."
-                %== $code->($line)
+                %= $code->($line)
               </div>
             % }
           % }
@@ -195,33 +195,33 @@
     <div class="box infobox" id="request">
       <table>
         % my $req = $self->req;
-        %== $kv->(Method => $req->method)
+        %= $kv->(Method => $req->method)
         % my $url = $req->url;
-        %== $kv->(Path => $url->to_string)
-        %== $kv->(Base => $url->base->to_string)
-        %== $kv->(Parameters => dumper $req->params->to_hash)
-        %== $kv->(Stash => dumper $snapshot)
-        %== $kv->(Session => dumper session)
-        %== $kv->(Version => $req->version)
+        %= $kv->(Path => $url->to_string)
+        %= $kv->(Base => $url->base->to_string)
+        %= $kv->(Parameters => dumper $req->params->to_hash)
+        %= $kv->(Stash => dumper $snapshot)
+        %= $kv->(Session => dumper session)
+        %= $kv->(Version => $req->version)
         % for my $name (@{$self->req->headers->names}) {
           % my $value = $self->req->headers->header($name);
-          %== $kv->($name, $value)
+          %= $kv->($name, $value)
         % }
       </table>
     </div>
     <div class="box infobox" id="more">
       <div id="infos">
         <table>
-          %== $kv->(Perl => "$] ($^O)")
+          %= $kv->(Perl => "$] ($^O)")
           % my $version  = $Mojolicious::VERSION;
           % my $codename = $Mojolicious::CODENAME;
-          %== $kv->(Mojolicious => "$version ($codename)")
-          %== $kv->(Home => app->home)
-          %== $kv->(Include => dumper \@INC)
-          %== $kv->(PID => $$)
-          %== $kv->(Name => $0)
-          %== $kv->(Executable => $^X)
-          %== $kv->(Time => scalar localtime(time))
+          %= $kv->(Mojolicious => "$version ($codename)")
+          %= $kv->(Home => app->home)
+          %= $kv->(Include => dumper \@INC)
+          %= $kv->(PID => $$)
+          %= $kv->(Name => $0)
+          %= $kv->(Executable => $^X)
+          %= $kv->(Time => scalar localtime(time))
         </table>
       </div>
       <div class="tap">tap for more</div>
@@ -63,12 +63,12 @@
       <ul>
         % for my $section (@$sections) {
           <li>
-            %== $link->(splice @$section, 0, 2)
+            %= $link->(splice @$section, 0, 2)
             % if (@$section) {
               <ul>
                 % while (@$section) {
                   <li>
-                    %== $link->(splice @$section, 0, 2)
+                    %= $link->(splice @$section, 0, 2)
                   </li>
                 % }
               </ul>
@@ -3,6 +3,7 @@ use Mojo::Base 'Mojo';
 
 use Carp 'croak';
 use Mojolicious::Commands;
+use Mojolicious::Controller;
 use Mojolicious::Plugins;
 use Mojolicious::Renderer;
 use Mojolicious::Routes;
@@ -25,14 +26,14 @@ has secret   => sub {
   $self->log->debug('Your secret passphrase needs to be changed!!!');
 
   # Default to application name
-  return ref $self;
+  ref $self;
 };
 has sessions => sub { Mojolicious::Sessions->new };
 has static   => sub { Mojolicious::Static->new };
 has types    => sub { Mojolicious::Types->new };
 
 our $CODENAME = 'Smiling Face With Sunglasses';
-our $VERSION  = '1.42';
+our $VERSION  = '1.49';
 
 # "These old doomsday devices are dangerously unstable.
 #  I'll rest easier not knowing where they are."
@@ -46,18 +47,8 @@ sub AUTOLOAD {
   croak qq/Can't locate object method "$method" via package "$package"/
     unless my $helper = $self->renderer->helpers->{$method};
 
-  # Load controller class
-  my $class = $self->controller_class;
-  if (my $e = Mojo::Loader->load($class)) {
-    $self->log->error(
-      ref $e
-      ? qq/Can't load controller class "$class": $e/
-      : qq/Controller class "$class" doesn't exist./
-    );
-  }
-
   # Call helper with fresh controller
-  return $class->new(app => $self)->$helper(@_);
+  $self->controller_class->new(app => $self)->$helper(@_);
 }
 
 sub DESTROY { }
@@ -73,19 +64,18 @@ sub new {
       my $self = shift;
       my $tx   = Mojo::Transaction::HTTP->new;
       $self->plugins->run_hook(after_build_tx => ($tx, $self));
-      return $tx;
+      $tx;
     }
   );
 
+  # Root directories
+  my $home = $self->home;
+  $self->renderer->root($home->rel_dir('templates'));
+  $self->static->root($home->rel_dir('public'));
+
+  # Default to application namespace
   my $r = $self->routes;
   $r->namespace(ref $self);
-  my $renderer = $self->renderer;
-  my $static   = $self->static;
-  my $home     = $self->home;
-
-  # Root
-  $renderer->root($home->rel_dir('templates'));
-  $static->root($home->rel_dir('public'));
 
   # Hide own controller methods
   $r->hide(qw/AUTOLOAD DESTROY client cookie delayed finish finished/);
@@ -101,7 +91,8 @@ sub new {
     if -w $home->rel_file('log');
 
   # Load default plugins
-  $self->plugin('agent_condition');
+  $self->plugin('callback_condition');
+  $self->plugin('header_condition');
   $self->plugin('default_helpers');
   $self->plugin('tag_helpers');
   $self->plugin('epl_renderer');
@@ -119,7 +110,7 @@ sub new {
   # Startup
   $self->startup(@_);
 
-  return $self;
+  $self;
 }
 
 # "Amy, technology isn't intrinsically good or evil. It's how it's used.
@@ -140,7 +131,7 @@ sub defaults {
     $self->{defaults}->{$key} = $values->{$key};
   }
 
-  return $self;
+  $self;
 }
 
 # The default dispatchers with exception handling
@@ -163,23 +154,16 @@ sub dispatch {
   return if $res->code;
   if (my $code = ($tx->req->error)[1]) { $res->code($code) }
   elsif ($tx->is_websocket) { $res->code(426) }
-  if ($self->routes->dispatch($c)) { $c->render_not_found unless $res->code }
+  unless ($self->routes->dispatch($c)) {
+    $c->render_not_found
+      unless $res->code;
+  }
 }
 
 # "Bite my shiny metal ass!"
 sub handler {
   my ($self, $tx) = @_;
 
-  # Load controller class
-  my $class = $self->controller_class;
-  if (my $e = Mojo::Loader->load($class)) {
-    $self->log->error(
-      ref $e
-      ? qq/Can't load controller class "$class": $e/
-      : qq/Controller class "$class" doesn't exist./
-    );
-  }
-
   # Embedded application
   my $stash = {};
   if ($tx->can('stash')) {
@@ -190,7 +174,8 @@ sub handler {
   # Build default controller and process
   my $defaults = $self->defaults;
   @{$stash}{keys %$defaults} = values %$defaults;
-  my $c = $class->new(app => $self, stash => $stash, tx => $tx);
+  my $c =
+    $self->controller_class->new(app => $self, stash => $stash, tx => $tx);
   unless (eval { $self->on_process->($self, $c); 1 }) {
     $self->log->fatal("Processing request failed: $@");
     $tx->res->code(500);
@@ -198,8 +183,7 @@ sub handler {
   }
 
   # Delayed
-  $self->log->debug(
-    'Waiting for delayed response, forgot to render or resume?')
+  $self->log->debug('Nothing has been rendered, assuming delayed response.')
     unless $stash->{'mojo.rendered'} || $tx->is_writing;
 }
 
@@ -239,7 +223,7 @@ sub start {
   $ENV{MOJO_APP} = ref $class ? $class : $class->new;
 
   # Start!
-  return Mojolicious::Commands->start(@_);
+  Mojolicious::Commands->start(@_);
 }
 
 # This will run once at startup
@@ -320,12 +304,12 @@ TLS, Bonjour, IDNA, Comet (long polling), chunking and multipart support.
 
 =item *
 
-Builtin async IO web server supporting epoll, kqueue, UNIX domain sockets and
-hot deployment, perfect for embedding.
+Built-in async IO web server supporting epoll, kqueue, UNIX domain sockets
+and hot deployment, perfect for embedding.
 
 =item *
 
-Automatic CGI, FastCGI, and L<PSGI> detection.
+Automatic CGI, FastCGI and L<PSGI> detection.
 
 =item *
 
@@ -339,7 +323,7 @@ Fresh code based upon years of experience developing L<Catalyst>.
 
 =head2 Installation
 
-All you need is a oneliner.
+All you need is a oneliner, it takes less than a minute.
 
   sudo sh -c "curl -L cpanmin.us | perl - Mojolicious"
 
@@ -349,12 +333,12 @@ These three lines are a whole web application.
 
   use Mojolicious::Lite;
 
-  get '/' => sub { shift->render_text('Hello World!') };
+  get '/' => {text => 'Hello World!'};
 
   app->start;
 
-To run this example with the built-in development server just put the code
-into a file and execute it with C<perl>.
+To run this example with the built-in development web server just put the
+code into a file and execute it with C<perl>.
 
   % perl hello.pl daemon
   Server available at http://127.0.0.1:3000.
@@ -369,16 +353,18 @@ Web development for humans, making hard things possible and everything fun.
   use Mojolicious::Lite;
 
   # Simple plain text response
-  get '/' => sub { shift->render_text('Hello World!') };
+  get '/' => sub {
+    my $self = shift;
+    $self->render_text('Hello World!');
+  };
 
   # Route associating the "/time" URL to template in DATA section
   get '/time' => 'clock';
 
   # RESTful web service sending JSON responses
-  get '/:offset' => sub {
-    my $self   = shift;
-    my $offset = $self->param('offset') || 23;
-    $self->render_json({list => [0 .. $offset]});
+  get '/list/:offset' => sub {
+    my $self = shift;
+    $self->render_json({list => [0 .. $self->param('offset')]});
   };
 
   # Scrape and return information from remote sites
@@ -416,16 +402,18 @@ A controller collects several actions together.
   use Mojo::Base 'Mojolicious::Controller';
 
   # Plain text response
-  sub hello { shift->render_text('Hello World!') }
+  sub hello {
+    my $self = shift;
+    $self->render_text('Hello World!');
+  }
 
   # Render external template "templates/example/clock.html.ep"
-  sub clock { shift->render }
+  sub clock { }
 
   # RESTful web service sending JSON responses
   sub restful {
-    my $self   = shift;
-    my $offset = $self->param('offset') || 23;
-    $self->render_json({list => [0 .. $offset]});
+    my $self = shift;
+    $self->render_json({list => [0 .. $self->param('offset')]});
   }
 
   # Scrape and return information from remote sites
@@ -473,7 +461,7 @@ especially when working in a team.
     # (paths are relative to the controller)
     $example->get('/')->to('#hello');
     $example->get('/time')->to('#clock');
-    $example->get('/:offset')->to('#restful');
+    $example->get('/list/:offset')->to('#restful');
 
     # All common HTTP verbs are supported
     $example->post('/title')->to('#title');
@@ -783,9 +771,9 @@ examples.
 
 =over 2
 
-=item L<Mojolicious::Plugin::AgentCondition>
+=item L<Mojolicious::Plugin::CallbackCondition>
 
-Route condition for C<User-Agent> headers.
+Very versatile route condition for arbitrary callbacks.
 
 =item L<Mojolicious::Plugin::Charset>
 
@@ -819,6 +807,10 @@ Internationalization helpers.
 
 JSON configuration files.
 
+=item L<Mojolicious::Plugin::Mount>
+
+Mount whole L<Mojolicious> applications.
+
 =item L<Mojolicious::Plugin::PodRenderer>
 
 Renderer for POD files and documentation browser.
@@ -856,6 +848,15 @@ startup.
     my $self = shift;
   }
 
+=head1 HELPERS
+
+In addition to the attributes and methods above you can also call helpers on
+instances of L<Mojolicious>.
+This includes all helpers from L<Mojolicious::Plugin::DefaultHelpers> and
+L<Mojolicious::Plugin::TagHelpers>.
+
+  $app->log->debug($app->dumper({foo => 'bar'}));
+
 =head1 SUPPORT
 
 =head2 Web
@@ -900,9 +901,9 @@ development. jQuery is designed to change the way that you write JavaScript.
 
 Licensed under the MIT License, L<http://creativecommons.org/licenses/MIT>.
 
-=head2 Prettify.js
+=head2 prettify.js
 
-  Version 21-Jul-2010
+  Version 1-Jun-2011
 
 A Javascript module and CSS file that allows syntax highlighting of source
 code snippets in an html page.
@@ -957,6 +958,8 @@ Adam Kennedy
 
 Adriano Ferreira
 
+Al Newkirk
+
 Alex Salimon
 
 Alexey Likhatskiy
@@ -1085,6 +1088,8 @@ Robert Hicks
 
 Robin Lee
 
+Roland Lammel
+
 Ryan Jendoubi
 
 Sascha Kiefer
@@ -33,7 +33,7 @@ sub content_is {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::is($self->_get_content($tx), $value, $desc);
 
-  return $self;
+  $self;
 }
 
 sub content_isnt {
@@ -44,7 +44,7 @@ sub content_isnt {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::isnt($self->_get_content($tx), $value, $desc);
 
-  return $self;
+  $self;
 }
 
 sub content_like {
@@ -55,7 +55,7 @@ sub content_like {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::like($self->_get_content($tx), $regex, $desc);
 
-  return $self;
+  $self;
 }
 
 sub content_unlike {
@@ -66,7 +66,7 @@ sub content_unlike {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::unlike($self->_get_content($tx), $regex, $desc);
 
-  return $self;
+  $self;
 }
 
 # "Marge, I can't wear a pink shirt to work.
@@ -78,7 +78,7 @@ sub content_type_is {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::is($tx->res->headers->content_type,
     $type, "Content-Type: $type");
-  return $self;
+  $self;
 }
 
 sub content_type_isnt {
@@ -87,7 +87,7 @@ sub content_type_isnt {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::isnt($tx->res->headers->content_type,
     $type, "not Content-Type: $type");
-  return $self;
+  $self;
 }
 
 sub content_type_like {
@@ -98,7 +98,7 @@ sub content_type_like {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::like($tx->res->headers->content_type, $regex, $desc);
 
-  return $self;
+  $self;
 }
 
 sub content_type_unlike {
@@ -109,7 +109,7 @@ sub content_type_unlike {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::unlike($tx->res->headers->content_type, $regex, $desc);
 
-  return $self;
+  $self;
 }
 
 # "A job's a job. I mean, take me.
@@ -122,7 +122,7 @@ sub element_exists {
   $desc ||= qq/"$selector" exists/;
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::ok($self->tx->res->dom->at($selector), $desc);
-  return $self;
+  $self;
 }
 
 sub element_exists_not {
@@ -130,7 +130,7 @@ sub element_exists_not {
   $desc ||= qq/"$selector" exists not/;
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::ok(!$self->tx->res->dom->at($selector), $desc);
-  return $self;
+  $self;
 }
 
 sub get_ok  { shift->_request_ok('get',  @_) }
@@ -144,7 +144,7 @@ sub header_is {
   Test::More::is(scalar $tx->res->headers->header($name),
     $value, "$name: " . ($value ? $value : ''));
 
-  return $self;
+  $self;
 }
 
 sub header_isnt {
@@ -155,7 +155,7 @@ sub header_isnt {
   Test::More::isnt(scalar $tx->res->headers->header($name),
     $value, "not $name: " . ($value ? $value : ''));
 
-  return $self;
+  $self;
 }
 
 sub header_like {
@@ -166,7 +166,7 @@ sub header_like {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::like(scalar $tx->res->headers->header($name), $regex, $desc);
 
-  return $self;
+  $self;
 }
 
 sub header_unlike {
@@ -177,7 +177,7 @@ sub header_unlike {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::unlike(scalar $tx->res->headers->header($name), $regex, $desc);
 
-  return $self;
+  $self;
 }
 
 sub json_content_is {
@@ -188,7 +188,7 @@ sub json_content_is {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::is_deeply($tx->res->json, $struct, $desc);
 
-  return $self;
+  $self;
 }
 
 # "God bless those pagans."
@@ -207,7 +207,7 @@ sub post_form_ok {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::ok($self->tx->is_done, $desc);
 
-  return $self;
+  $self;
 }
 
 # "WHO IS FONZY!?! Don't they teach you anything at school?"
@@ -218,7 +218,7 @@ sub reset_session {
   $self->ua->cookie_jar->empty;
   $self->ua->max_redirects($self->max_redirects);
   $self->tx(undef);
-  return $self;
+  $self;
 }
 
 # "Internet! Is that thing still around?"
@@ -230,7 +230,7 @@ sub status_is {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::is($self->tx->res->code, $status, "$status $message");
 
-  return $self;
+  $self;
 }
 
 sub status_isnt {
@@ -241,7 +241,7 @@ sub status_isnt {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::isnt($self->tx->res->code, $status, "not $status $message");
 
-  return $self;
+  $self;
 }
 
 sub text_is {
@@ -255,7 +255,7 @@ sub text_is {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::is($text, $value, $desc);
 
-  return $self;
+  $self;
 }
 
 sub text_isnt {
@@ -269,7 +269,7 @@ sub text_isnt {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::isnt($text, $value, $desc);
 
-  return $self;
+  $self;
 }
 
 # "Hello, my name is Barney Gumble, and I'm an alcoholic.
@@ -286,7 +286,7 @@ sub text_like {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::like($text, $regex, $desc);
 
-  return $self;
+  $self;
 }
 
 sub text_unlike {
@@ -300,7 +300,7 @@ sub text_unlike {
   local $Test::Builder::Level = $Test::Builder::Level + 1;
   Test::More::unlike($text, $regex, $desc);
 
-  return $self;
+  $self;
 }
 
 sub _get_content {
@@ -314,29 +314,27 @@ sub _get_content {
   # Content
   my $content = $tx->res->body;
   decode $charset, $content if $charset;
-  return $content;
+  $content;
 }
 
 # "Are you sure this is the Sci-Fi Convention? It's full of nerds!"
 sub _request_ok {
   my ($self, $method, $url, $headers, $body) = @_;
-
-  my $desc = "$method $url";
-  utf8::encode $desc;
-
-  # Body without headers
   $body = $headers if !ref $headers && @_ > 3;
   $headers = {} if !ref $headers;
 
+  # Perform request against application
   my $ua = $self->ua;
   $ua->app($self->app);
   $ua->max_redirects($self->max_redirects);
   $self->tx($ua->$method($url, %$headers, $body));
   local $Test::Builder::Level = $Test::Builder::Level + 2;
   my ($error, $code) = $self->tx->error;
+  my $desc = "$method $url";
+  utf8::encode $desc;
   Test::More::ok(!$error || $code, $desc);
 
-  return $self;
+  $self;
 }
 
 1;
@@ -69,7 +69,7 @@ sub _request {
   my ($message, $code) = $tx->error;
   warn qq/Problem loading URL "$_[0]". ($message)\n/ if $message && !$code;
 
-  return $tx->res;
+  $tx->res;
 }
 
 1;
@@ -58,7 +58,8 @@ hypnotoad - Hypnotoad HTTP 1.1 And WebSocket Server
 
 =head1 SYNOPSIS
 
-    hypnotoad myapp.pl
+  % hypnotoad --help
+  % hypnotoad myapp.pl
 
 =head1 DESCRIPTION
 
@@ -28,7 +28,7 @@ mojo - The Mojolicious Command System
 
 =head1 SYNOPSIS
 
-    mojo help
+  % mojo --help
 
 =head1 DESCRIPTION
 
@@ -0,0 +1,79 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use File::Basename 'dirname';
+use File::Spec;
+
+use lib join '/', File::Spec->splitdir(dirname(__FILE__)), 'lib';
+use lib join '/', File::Spec->splitdir(dirname(__FILE__)), '..', 'lib';
+
+# Check if Mojo is installed
+eval 'use Mojo::Server::Morbo';
+die <<EOF if $@;
+It looks like you don't have the Mojolicious Framework installed.
+Please visit http://mojolicio.us for detailed installation instructions.
+
+EOF
+
+use Getopt::Long 'GetOptions';
+
+# "Welcome to 'Entertainment And Earth Invasion Tonite'.
+#  Across the galaxy my people are completing the mighty space fleet that
+#  will exterminate the human race!
+#  But first, this news from Tinseltown."
+my $morbo = Mojo::Server::Morbo->new;
+my ($help, @listen, @watch);
+GetOptions(
+  help => sub { $help = 1 },
+  'listen=s' => \@listen,
+  'watch=s'  => \@watch
+);
+$help = 1 unless my $app = shift;
+
+# Usage
+die <<"EOF" if $help;
+usage: $0 [OPTIONS] [APPLICATION]
+
+  morbo script/myapp
+  morbo myapp.pl
+
+These options are available:
+  --help                     Show this message.
+  --listen <location>        Set one or more locations you want to listen on,
+                             defaults to http://*:3000.
+  --watch <directory/file>   Set one or more directories and files to watch
+                             for changes, defaults to the application script
+                             as well as the "lib" and "templates" directories
+                             in the current working directory.
+EOF
+
+# "With Halley's Comet out of ice, Earth is experiencing a sudden case of
+#  global warming.
+#  Morbo is pleased but sticky."
+$morbo->listen(\@listen) if @listen;
+$morbo->watch(\@watch)   if @watch;
+$morbo->run($app);
+
+__END__
+
+=head1 NAME
+
+morbo - Morbo HTTP 1.1 And WebSocket Development Server
+
+=head1 SYNOPSIS
+
+  % morbo --help
+  % morbo myapp.pl
+
+=head1 DESCRIPTION
+
+Start L<Mojolicious> and L<Mojolicious::Lite> applications with the
+L<Mojo::Server::Morbo> web server.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
+
+=cut
@@ -12,7 +12,7 @@ use Test::More;
 
 plan skip_all => 'Perl 5.10 or Digest::SHA required for this test!'
   unless eval { require Digest::SHA; 1 };
-plan tests => 106;
+plan tests => 112;
 
 use_ok 'Mojo::Util',       'md5_bytes';
 use_ok 'Mojo::ByteStream', 'b';
@@ -37,6 +37,17 @@ is $stream->decamelize, 'foo_b_b', 'right decamelized result';
 $stream = b('Foo::BB');
 is $stream->decamelize, 'foo-b_b', 'right decamelized result';
 
+# camelize/decamelize roundtrip
+my $original = 'MyApp::Controller::FooBAR';
+$stream = b($original);
+my $result = $stream->decamelize->to_string;
+is "$stream", $result, 'stringified successfully';
+isnt $result, $original, 'decamelized result is different';
+is $stream->camelize,   $original, 'successful roundtrip';
+is $stream->decamelize, $result,   'right decamelized result';
+isnt "$stream", $original, 'decamelized result is different';
+is $stream->camelize, $original, 'successful roundtrip again';
+
 # b64_encode
 $stream = b('foobar$%^&3217');
 is $stream->b64_encode, "Zm9vYmFyJCVeJjMyMTc=\n",
@@ -98,8 +109,8 @@ $stream = b('"foo 23 \"bar"');
 is $stream->unquote, 'foo 23 "bar', 'right unquoted result';
 
 # md5_bytes
-my $original = 'foo bar baz ♥';
-my $copy     = $original;
+$original = 'foo bar baz ♥';
+my $copy = $original;
 $stream = b($copy);
 is unpack('H*', $stream->md5_bytes), "a740aeb6e066f158cbf19fd92e890d2d",
   'right binary md5 checksum';
@@ -130,7 +141,7 @@ $stream = b('0');
 is $stream->size,      1,   'size is 1';
 is $stream->to_string, '0', 'right content';
 
-# hmac_md5_sum (RFC2202)
+# hmac_md5_sum (RFC 2202)
 is b("Hi There")->hmac_md5_sum(chr(0x0b) x 16),
   '9294727a3638bb1c13f48ef8158bfc9d', 'right hmac md5 checksum';
 is b("what do ya want for nothing?")->hmac_md5_sum("Jefe"),
@@ -151,7 +162,7 @@ is b(
   ->hmac_md5_sum(chr(0xaa) x 80), '6f630fad67cda0ee1fb1f562db3aa53e',
   'right hmac md5 checksum';
 
-# hmac_sha1_sum (RFC2202)
+# hmac_sha1_sum (RFC 2202)
 is b("Hi There")->hmac_sha1_sum(chr(0x0b) x 20),
   'b617318655057264e28bc0b6fb378c8ef146be00', 'right hmac sha1 checksum';
 is b("what do ya want for nothing?")->hmac_sha1_sum("Jefe"),
@@ -200,8 +211,8 @@ is "$stream", 'foobar&lt;baz&gt;&amp;&quot;&OElig;',
 
 # utf8 html_unescape
 $stream =
-  b('foobar&lt;baz&gt;&#x26;&#34;&OElig;')->decode('UTF-8')->html_unescape;
-is "$stream", "foobar<baz>&\"\x{152}", 'right html unescaped result';
+  b('foo&lt;baz&gt;&#x26;&#34;&OElig;&Foo;')->decode('UTF-8')->html_unescape;
+is "$stream", "foo<baz>&\"\x{152}&Foo;", 'right html unescaped result';
 
 # html_escape (path)
 $stream =
@@ -3,7 +3,11 @@
 use strict;
 use warnings;
 
-use Test::More tests => 7;
+use Test::More tests => 17;
+
+use Cwd 'cwd';
+use File::Spec;
+use File::Temp;
 
 # "My cat's breath smells like cat food."
 use_ok 'Mojo::Command';
@@ -36,3 +40,45 @@ is $command->get_data('template4', 'Example::Package::Windows'),
 is_deeply [sort keys %{$command->get_all_data('Example::Package::Windows')}],
   [qw/template3 template4/], 'right DATA files';
 close $data;
+
+# Class to file and path
+is $command->class_to_file('Foo::Bar'), 'foo_bar',    'right file';
+is $command->class_to_path('Foo::Bar'), 'Foo/Bar.pm', 'right path';
+
+# Environment detection
+{
+  local $ENV{PLACK_ENV} = 'production';
+  is $command->detect, 'psgi', 'right environment';
+}
+{
+  local $ENV{PATH_INFO} = '/test';
+  is $command->detect, 'cgi', 'right environment';
+}
+{
+  local $ENV{GATEWAY_INTERFACE} = 'CGI/1.1';
+  is $command->detect, 'cgi', 'right environment';
+}
+{
+  local %ENV = ();
+  is $command->detect, 'fastcgi', 'right environment';
+}
+
+# Generating files
+my $cwd = cwd;
+my $dir = File::Temp::tempdir(CLEANUP => 1);
+chdir $dir;
+$command->create_rel_dir('foo/bar');
+is -d File::Spec->catdir($dir, qw/foo bar/), 1, 'directory exists';
+my $template = "@@ foo_bar\njust <%= 'works' %>!\n";
+open $data, '<', \$template;
+no strict 'refs';
+*{"Mojo::Command::DATA"} = $data;
+$command->render_to_rel_file('foo_bar', 'bar/baz.txt');
+open my $txt, '<', $command->rel_file('bar/baz.txt');
+is join('', <$txt>), "just works!\n", 'right result';
+$command->chmod_rel_file('bar/baz.txt', 0700);
+is -e $command->rel_file('bar/baz.txt'), 1, 'file is executable';
+$command->write_rel_file('123.xml', "seems\nto\nwork");
+open my $xml, '<', $command->rel_file('123.xml');
+is join('', <$xml>), "seems\nto\nwork", 'right result';
+chdir $cwd;
@@ -3,7 +3,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 30;
+use Test::More tests => 34;
 
 # "Hello, my name is Mr. Burns. I believe you have a letter for me.
 #  Okay Mr. Burns, what’s your first name.
@@ -23,6 +23,14 @@ $jar->add(
     value  => 'bar'
   )
 );
+$jar->add(
+  Mojo::Cookie::Response->new(
+    domain => '.kraih.com',
+    path   => '/',
+    name   => 'just',
+    value  => 'works'
+  )
+);
 my @cookies = $jar->find(Mojo::URL->new('http://kraih.com/foo'));
 is $cookies[0]->name,  'foo', 'right name';
 is $cookies[0]->value, 'bar', 'right value';
@@ -52,8 +60,8 @@ my $expired = Mojo::Cookie::Response->new(
 $expired->expires(time - 1);
 $jar->add($expired);
 @cookies = $jar->find(Mojo::URL->new('http://labs.kraih.com/foo'));
-is $cookies[0]->name,  'foo', 'right name';
-is $cookies[0]->value, 'bar', 'right value';
+is $cookies[0]->name,  'just',  'right name';
+is $cookies[0]->value, 'works', 'right value';
 is $cookies[1], undef, 'no second cookie';
 
 # Multiple cookies
@@ -67,10 +75,10 @@ $jar->add(
   )
 );
 @cookies = $jar->find(Mojo::URL->new('http://labs.kraih.com/foo'));
-is $cookies[0]->name,  'baz', 'right name';
-is $cookies[0]->value, '23',  'right value';
-is $cookies[1]->name,  'foo', 'right name';
-is $cookies[1]->value, 'bar', 'right value';
+is $cookies[0]->name,  'baz',   'right name';
+is $cookies[0]->value, '23',    'right value';
+is $cookies[1]->name,  'just',  'right name';
+is $cookies[1]->value, 'works', 'right value';
 is $cookies[2], undef, 'no third cookie';
 
 # Multiple cookies with leading dot
@@ -83,11 +91,13 @@ $jar->add(
   )
 );
 @cookies = $jar->find(Mojo::URL->new('http://labs.kraih.com/fo'));
-is $cookies[0]->name,  'baz',  'right name';
-is $cookies[0]->value, '23',   'right value';
-is $cookies[1]->name,  'this', 'right name';
-is $cookies[1]->value, 'that', 'right value';
-is $cookies[2], undef, 'no third cookie';
+is $cookies[0]->name,  'baz',   'right name';
+is $cookies[0]->value, '23',    'right value';
+is $cookies[1]->name,  'just',  'right name';
+is $cookies[1]->value, 'works', 'right value';
+is $cookies[2]->name,  'this',  'right name';
+is $cookies[2]->value, 'that',  'right value';
+is $cookies[3], undef, 'no fourth cookie';
 
 # Replace cookie
 $jar = Mojo::CookieJar->new;
@@ -126,6 +136,7 @@ $jar->add(
 is $cookies[0], undef, 'no cookie for port 80';
 @cookies = $jar->find(Mojo::URL->new('http://kraih.com:88/foo'));
 is $cookies[0]->value, 'bar', 'cookie for port 88';
+is $cookies[1], undef, 'no second cookie';
 
 # Switch between secure and normal cookies
 $jar = Mojo::CookieJar->new;
@@ -154,3 +165,4 @@ $jar->add(
 is $cookies[0]->value, 'bar', 'right value';
 @cookies = $jar->find(Mojo::URL->new('https://kraih.com/foo'));
 is $cookies[0]->value, 'bar', 'right value';
+is $cookies[1], undef, 'no second cookie';
@@ -8,11 +8,11 @@ use Test::More tests => 10;
 # "Can't we have one meeting that doesn't end with digging up a corpse?"
 use_ok 'Mojo::Date';
 
-# RFC822/1123
+# RFC 822/1123
 my $date = Mojo::Date->new('Sun, 06 Nov 1994 08:49:37 GMT');
 is $date->epoch, 784111777, 'right epoch value';
 
-# RFC850/1036
+# RFC 850/1036
 is $date->parse('Sunday, 06-Nov-94 08:49:37 GMT')->epoch,
   784111777, 'right epoch value';
 
@@ -5,7 +5,7 @@ use warnings;
 
 use utf8;
 
-use Test::More tests => 591;
+use Test::More tests => 597;
 
 # "Homer gave me a kidney: it wasn't his, I didn't need it,
 #  and it came postage due- but I appreciated the gesture!"
@@ -1309,6 +1309,25 @@ is $dom->find('body > ul > li > p')->[2]->text, '',           'no text';
 is $dom->find('body > ul > li')->[2]->all_text, 'Test 3 2 1', 'right text';
 is $dom->find('body > ul > li > p')->[2]->all_text, '', 'no text';
 
+# Advanced whitespace trimming (punctuation)
+$dom = Mojo::DOM->new->parse(<<EOF);
+<html>
+  <head>
+    <title>Real World!</title>
+  <body>
+    <div>foo <strong>bar</strong>.</div>
+    <div>foo<strong>, bar</strong>baz<strong>; yada</strong>.</div>
+    <div>foo<strong>: bar</strong>baz<strong>? yada</strong>!</div>
+EOF
+is $dom->find('html > head > title')->[0]->text, 'Real World!', 'right text';
+is $dom->find('body > div')->[0]->all_text,      'foo bar.',    'right text';
+is $dom->find('body > div')->[1]->all_text, 'foo, bar baz; yada.',
+  'right text';
+is $dom->find('body > div')->[1]->text, 'foo baz.', 'right text';
+is $dom->find('body > div')->[2]->all_text, 'foo: bar baz? yada!',
+  'right text';
+is $dom->find('body > div')->[2]->text, 'foo baz!', 'right text';
+
 # Real world JavaScript and CSS
 $dom = Mojo::DOM->new->parse(<<EOF);
 <html>
@@ -8,7 +8,6 @@ BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
 
 use Test::More;
 
-# "I ate the blue ones... they taste like burning."
 use File::Spec;
 use File::Temp;
 use FindBin;
@@ -22,8 +21,7 @@ plan skip_all => 'set TEST_HYPNOTOAD to enable this test (developer only!)'
   unless $ENV{TEST_HYPNOTOAD};
 plan tests => 40;
 
-# "Daddy, I'm scared. Too scared to even wet my pants.
-#  Just relax and it'll come, son."
+# "I ate the blue ones... they taste like burning."
 use_ok 'Mojo::Server::Hypnotoad';
 
 # Config
@@ -6,17 +6,27 @@ use warnings;
 # Disable IPv6, epoll and kqueue
 BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
 
-use Test::More tests => 11;
-
-use_ok 'Mojo::IOLoop';
-
-use IO::Handle;
+use Test::More tests => 14;
 
 # "Marge, you being a cop makes you the man!
 #  Which makes me the woman, and I have no interest in that,
 #  besides occasionally wearing the underwear,
 #  which as we discussed, is strictly a comfort thing."
+use_ok 'Mojo::IOLoop';
+
+use IO::Handle;
+
+# Custom watcher
+package MyWatcher;
+use Mojo::Base 'Mojo::IOWatcher';
+
+package main;
+Mojo::IOLoop->singleton->iowatcher(MyWatcher->new);
+
+# Watcher inheritance
 my $loop = Mojo::IOLoop->new;
+Mojo::IOLoop->iowatcher(MyWatcher->new);
+is ref $loop->iowatcher, 'MyWatcher', 'right class';
 
 # Readonly handle
 my $ro = IO::Handle->new;
@@ -123,4 +133,32 @@ Mojo::IOLoop->idle(sub { Mojo::IOLoop->stop if $idle++ });
 Mojo::IOLoop->start;
 is $idle, 2, 'two idle ticks';
 
+# Dropped listen socket
+$port = Mojo::IOLoop->generate_port;
+$id = $loop->listen(port => $port);
+$loop->connect(
+  address    => 'localhost',
+  port       => $port,
+  on_connect => sub {
+    my $loop = shift;
+    $loop->drop($id);
+    $loop->stop;
+  }
+);
+$loop->start;
+$error = undef;
+my $connected;
+$loop->connect(
+  address    => 'localhost',
+  port       => $port,
+  on_connect => sub { $connected = 1 },
+  on_error   => sub {
+    shift->stop;
+    $error = pop;
+  }
+);
+$loop->start;
+ok $error, 'has error';
+ok !$connected, 'not connected';
+
 __DATA__
@@ -1,177 +0,0 @@
-#!/usr/bin/env perl
-
-use strict;
-use warnings;
-
-# Disable IPv6, epoll and kqueue
-BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
-
-use Test::More;
-plan skip_all => 'set TEST_ONLINE to enable this test (developer only!)'
-  unless $ENV{TEST_ONLINE};
-plan tests => 18;
-
-use_ok 'Mojo::IOLoop';
-
-use List::Util 'first';
-use Mojo::URL;
-
-# "Your guilty consciences may make you vote Democratic, but secretly you all
-#  yearn for a Republican president to lower taxes, brutalize criminals, and
-#  rule you like a king!"
-my $loop = Mojo::IOLoop->singleton;
-
-# Resolve all record
-my %types;
-$loop->resolve(
-  'www.google.com',
-  '*',
-  sub {
-    my ($self, $records) = @_;
-    $types{$_->[0]}++ for @$records;
-    $self->stop;
-  }
-)->start;
-ok keys %types > 1, 'multiple record types';
-
-# Lookup
-my $result;
-Mojo::IOLoop->lookup(
-  'google.com',
-  sub {
-    my ($self, $address) = @_;
-    $result = $address;
-    $self->stop;
-  }
-)->start;
-ok $result, 'got an address';
-
-# Resolve TXT record
-$result = undef;
-Mojo::IOLoop->resolve(
-  'google.com',
-  'TXT',
-  sub {
-    my ($self, $records) = @_;
-    $result = (first { $_->[0] eq 'TXT' } @$records)->[1];
-    $self->stop;
-  }
-)->start;
-like $result, qr/spf/, 'right record';
-
-# Resolve NS records
-my $found = 0;
-$loop->resolve(
-  'gmail.com',
-  'NS',
-  sub {
-    my ($self, $records) = @_;
-    $found++ if first { $_->[1] =~ /ns\d*.google\.com/ } @$records;
-    $self->stop;
-  }
-)->start;
-ok $found, 'found NS records';
-
-# Resolve AAAA record
-$result = undef;
-my $ttl;
-$loop->resolve(
-  'ipv6.google.com',
-  'AAAA',
-  sub {
-    my ($self, $records) = @_;
-    $result = (first { $_->[0] eq 'AAAA' } @$records)->[1];
-    $ttl    = (first { $_->[0] eq 'AAAA' } @$records)->[2];
-    $self->stop;
-  }
-)->start;
-like $result, $Mojo::URL::IPV6_RE, 'valid IPv6 record';
-ok $ttl, 'got a TTL value';
-
-# Resolve CNAME record
-$result = undef;
-$loop->resolve(
-  'ipv6.google.com',
-  'CNAME',
-  sub {
-    my ($self, $records) = @_;
-    $result = (first { $_->[0] eq 'CNAME' } @$records)->[1];
-    $self->stop;
-  }
-)->start;
-is $result, 'ipv6.l.google.com', 'right CNAME record';
-
-# Resolve MX records
-$found = 0;
-$loop->resolve(
-  'gmail.com',
-  'MX',
-  sub {
-    my ($self, $records) = @_;
-    $found++
-      if first { $_->[1] =~ /gmail-smtp-in\.l\.google\.com/ } @$records;
-    $self->stop;
-  }
-)->start;
-ok $found, 'found MX records';
-
-# Resolve A record and perform PTR roundtrip
-my ($a1, $ptr, $a2);
-$loop->resolve(
-  'perl.org',
-  'A',
-  sub {
-    my ($self, $records) = @_;
-    $a1 = (first { $_->[0] eq 'A' } @$records)->[1];
-    $self->resolve(
-      $a1, 'PTR',
-      sub {
-        my ($self, $records) = @_;
-        $ptr = $records->[0]->[1];
-        $self->resolve(
-          $ptr, 'A',
-          sub {
-            my ($self, $records) = @_;
-            $a2 = (first { $_->[0] eq 'A' } @$records)->[1];
-            $self->stop;
-          }
-        );
-      }
-    );
-  }
-)->start;
-like $a1, $Mojo::URL::IPV4_RE, 'valid IPv4 record';
-is $a1, $a2, 'PTR roundtrip succeeded';
-
-# Resolve PTR record (IPv6)
-$found = 0;
-$loop->resolve(
-  '2001:4f8:0:2:0:0:0:e',
-  'PTR',
-  sub {
-    my ($self, $records) = @_;
-    $found++ if first { $_->[1] eq 'freebsd.isc.org' } @$records;
-    $self->stop;
-  }
-)->start;
-ok $found, 'found IPv6 PTR record';
-
-# Invalid DNS server
-ok scalar Mojo::IOLoop->dns_servers, 'got a dns server';
-Mojo::IOLoop->dns_servers('192.0.2.1', Mojo::IOLoop->dns_servers);
-is Mojo::IOLoop->dns_servers, '192.0.2.1', 'new invalid dns server';
-Mojo::IOLoop->lookup('google.com', sub { Mojo::IOLoop->stop })->start;
-my $fallback = Mojo::IOLoop->dns_servers;
-isnt $fallback, '192.0.2.1', 'valid dns server';
-$result = undef;
-Mojo::IOLoop->lookup(
-  'google.com',
-  sub {
-    my ($self, $address) = @_;
-    $result = $address;
-    $self->stop;
-  }
-)->start;
-ok $result, 'got an address';
-is scalar $loop->dns_servers, $fallback, 'still the same dns server';
-isnt $fallback, '192.0.2.1', 'still valid dns server';
@@ -3,7 +3,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 44;
+use Test::More tests => 34;
 
 use FindBin;
 use lib "$FindBin::Bin/lib";
@@ -68,30 +68,3 @@ ok !!LoaderTest::C->can('new'), 'loaded successfully';
 
 # Class does not exist
 ok $loader->load('LoaderTest'), 'nothing to load';
-
-# Reload
-my $file = IO::File->new;
-my $dir  = File::Temp::tempdir(CLEANUP => 1);
-my $path = File::Spec->catfile($dir, 'MojoTestReloader.pm');
-$file->open("> $path");
-$file->syswrite(
-  "package MojoTestReloader;\nsub test1 { 23 }\nsub test3 { 32 }\n1;");
-$file->close;
-push @INC, $dir;
-require MojoTestReloader;
-ok my $t1 = MojoTestReloader->can('test1'), 'loaded successfully';
-ok !MojoTestReloader->can('test2'), 'package is clean';
-is $t1->(), 23, 'right result';
-ok my $t3 = MojoTestReloader->can('test3'), 'loaded successfully';
-is $t3->(), 32, 'right result';
-sleep 2;
-$file->open("> $path");
-$file->syswrite(
-  "package MojoTestReloader;\nsub test2 { 26 }\nsub test3 { 62 }\n1;");
-$file->close;
-Mojo::Loader->reload;
-ok my $t2 = MojoTestReloader->can('test2'), 'loaded successfully';
-ok !MojoTestReloader->can('test1'), 'package is clean';
-is $t2->(), 26, 'right result';
-ok $t3 = MojoTestReloader->can('test3'), 'loaded successfully';
-is $t3->(), 62, 'right result';
@@ -147,7 +147,7 @@ is $req->leftovers, "GET / HTTP/1.1\x0d\x0a\x0d\x0a",
   'second request in leftovers';
 
 # Parse HTTP 1.1 start line, no headers and body with leading CRLFs
-# (SHOULD be ignored, RFC2616, Section 4.1)
+# (SHOULD be ignored, RFC 2616, Section 4.1)
 $req = Mojo::Message::Request->new;
 $req->parse("\x0d\x0aGET / HTTP/1.1\x0d\x0a\x0d\x0a");
 ok $req->is_done, 'request is done';
@@ -1529,7 +1529,7 @@ is $req->version, '1.0', 'right version';
 is $req->at_least_version('1.0'), 1,     'at least version 1.0';
 is $req->at_least_version('1.2'), undef, 'not version 1.2';
 is $req->body, 'hello=world', 'right content';
-is_deeply $req->param('hello'), 'world', 'right paramaters';
+is_deeply $req->param('hello'), 'world', 'right parameters';
 is $req->url->to_abs->to_string, 'http://localhost/test/index.cgi',
   'right absolute URL';
 
@@ -1769,7 +1769,7 @@ is $res2->cookie('baz')->value, 'yada',     'right value';
 is $res2->cookie('bar')->path,  '/test/23', 'right path';
 is $res2->cookie('bar')->value, 'baz',      'right value';
 
-# Build response with callback (make sure its called)
+# Build response with callback (make sure it's called)
 $res = Mojo::Message::Response->new;
 $res->code(200);
 $res->headers->content_length(10);
@@ -0,0 +1,153 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+# Disable IPv6, epoll and kqueue
+BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
+
+use Test::More;
+
+use Cwd 'cwd';
+use File::Temp;
+use FindBin;
+use IO::Socket::INET;
+use Mojo::Command;
+use Mojo::IOLoop;
+use Mojo::UserAgent;
+
+plan skip_all => 'set TEST_MORBO to enable this test (developer only!)'
+  unless $ENV{TEST_MORBO};
+plan tests => 28;
+
+# "Morbo wishes these stalwart nomads peace among the Dutch tulips.
+#  At least all those windmills will keep them cool.
+#  WINDMILLS DO NOT WORK THAT WAY! GOODNIGHT!"
+use_ok 'Mojo::Server::Morbo';
+
+# Prepare script
+my $cwd = cwd;
+my $dir = File::Temp::tempdir(CLEANUP => 1);
+chdir $dir;
+my $command = Mojo::Command->new;
+my $script  = $command->rel_file('myapp.pl');
+my $morbo   = Mojo::Server::Morbo->new;
+ok !$morbo->check_file($script), 'file has not changed';
+$command->write_rel_file('myapp.pl', <<EOF);
+use Mojolicious::Lite;
+
+app->log->level('fatal');
+
+get '/hello' => {text => 'Hello Morbo!'};
+
+app->start;
+EOF
+
+# Start
+my $port   = Mojo::IOLoop->generate_port;
+my $prefix = "$FindBin::Bin/../../script";
+my $pid    = open my $server, '-|', $^X, "$prefix/morbo", '--listen',
+  "http://*:$port", $script;
+sleep 1
+  while !IO::Socket::INET->new(
+  Proto    => 'tcp',
+  PeerAddr => 'localhost',
+  PeerPort => $port
+  );
+
+my $ua = Mojo::UserAgent->new;
+
+# Application is alive
+my $tx = $ua->get("http://127.0.0.1:$port/hello");
+ok $tx->is_done, 'transaction is done';
+is $tx->res->code, 200,            'right status';
+is $tx->res->body, 'Hello Morbo!', 'right content';
+
+# Same result
+$tx = $ua->get("http://127.0.0.1:$port/hello");
+ok $tx->is_done, 'transaction is done';
+is $tx->res->code, 200,            'right status';
+is $tx->res->body, 'Hello Morbo!', 'right content';
+
+# Update script without changing size
+my ($size, $mtime) = (stat $script)[7, 9];
+ok !$morbo->check_file($script), 'file has not changed';
+$command->write_rel_file('myapp.pl', <<EOF);
+use Mojolicious::Lite;
+
+app->log->level('fatal');
+
+get '/hello' => {text => 'Hello World!'};
+
+app->start;
+EOF
+ok $morbo->check_file($script), 'file has changed';
+ok((stat $script)[9] > $mtime, 'modify time has changed');
+is((stat $script)[7], $size, 'still equal size');
+sleep 3;
+sleep 1
+  while !IO::Socket::INET->new(
+  Proto    => 'tcp',
+  PeerAddr => 'localhost',
+  PeerPort => $port
+  );
+
+# Application has been reloaded
+$tx = $ua->get("http://127.0.0.1:$port/hello");
+ok $tx->is_done, 'transaction is done';
+is $tx->res->code, 200,            'right status';
+is $tx->res->body, 'Hello World!', 'right content';
+
+# Same result
+$tx = $ua->get("http://127.0.0.1:$port/hello");
+ok $tx->is_done, 'transaction is done';
+is $tx->res->code, 200,            'right status';
+is $tx->res->body, 'Hello World!', 'right content';
+
+# Update script without changing mtime
+($size, $mtime) = (stat $script)[7, 9];
+ok !$morbo->check_file($script), 'file has not changed';
+$command->write_rel_file('myapp.pl', <<EOF);
+use Mojolicious::Lite;
+
+app->log->level('fatal');
+
+get '/hello' => {text => 'Hello!'};
+
+app->start;
+EOF
+utime $mtime, $mtime, $script;
+ok $morbo->check_file($script), 'file has changed';
+ok((stat $script)[9] == $mtime, 'modify time has not changed');
+isnt((stat $script)[7], $size, 'size has changed');
+sleep 3;
+sleep 1
+  while !IO::Socket::INET->new(
+  Proto    => 'tcp',
+  PeerAddr => 'localhost',
+  PeerPort => $port
+  );
+
+# Application has been reloaded again
+$tx = $ua->get("http://127.0.0.1:$port/hello");
+ok $tx->is_done, 'transaction is done';
+is $tx->res->code, 200,      'right status';
+is $tx->res->body, 'Hello!', 'right content';
+
+# Same result
+$tx = $ua->get("http://127.0.0.1:$port/hello");
+ok $tx->is_done, 'transaction is done';
+is $tx->res->code, 200,      'right status';
+is $tx->res->body, 'Hello!', 'right content';
+
+# Stop
+kill 'INT', $pid;
+sleep 1
+  while IO::Socket::INET->new(
+  Proto    => 'tcp',
+  PeerAddr => 'localhost',
+  PeerPort => $port
+  );
+
+# Cleanup
+chdir $cwd;
@@ -5,7 +5,7 @@ use warnings;
 
 use utf8;
 
-use Test::More tests => 44;
+use Test::More tests => 63;
 
 # "Now that's a wave of destruction that's easy on the eyes."
 use_ok 'Mojo::Parameters';
@@ -16,6 +16,8 @@ my $params2 = Mojo::Parameters->new('x', 1, 'y', 2);
 is $params->pair_separator, '&',                 'right pair separator';
 is $params->to_string,      'foo=b%3Bar&baz=23', 'right format';
 is $params2->to_string,     'x=1&y=2',           'right format';
+is $params->to_string,      'foo=b%3Bar&baz=23', 'right format';
+is_deeply $params->params, ['foo', 'b;ar', 'baz', 23], 'right structure';
 $params->pair_separator(';');
 is $params->to_string, 'foo=b%3Bar;baz=23', 'right format';
 is "$params", 'foo=b%3Bar;baz=23', 'right format';
@@ -72,6 +74,20 @@ $params = Mojo::Parameters->new($params->to_string);
 is_deeply $params->param('foo'), 0, 'right structure';
 is $params->to_string, 'foo=0', 'right format';
 
+# Semicolon
+$params = Mojo::Parameters->new('foo=bar;baz');
+is $params->pair_separator, '&',           'right pair separator';
+is $params->to_string,      'foo=bar;baz', 'right format';
+is_deeply $params->params, [foo => 'bar', baz => ''], 'right structure';
+is_deeply $params->to_hash, {foo => 'bar', baz => ''}, 'right structure';
+is $params->pair_separator, ';',            'right pair separator';
+is $params->to_string,      'foo=bar;baz=', 'right format';
+$params = Mojo::Parameters->new('foo=bar%3Bbaz');
+is $params->pair_separator, '&', 'right pair separator';
+is_deeply $params->params, [foo => 'bar;baz'], 'right structure';
+is_deeply $params->to_hash, {foo => 'bar;baz'}, 'right structure';
+is $params->to_string, 'foo=bar%3Bbaz', 'right format';
+
 # Reconstruction
 $params = Mojo::Parameters->new('foo=bar&baz=23');
 is "$params", 'foo=bar&baz=23', 'right format';
@@ -89,6 +105,9 @@ $params->remove('c');
 is $params->to_string, "=c&=", 'right format';
 $params->remove(undef);
 ok !$params->to_string, 'empty';
+$params->parse('');
+ok !$params->to_string, 'empty';
+is_deeply $params->to_hash, {}, 'right structure';
 
 # +
 $params = Mojo::Parameters->new('foo=%2B');
@@ -101,6 +120,11 @@ is_deeply $params->to_hash, {foo => '+'}, 'right structure';
 $params->append('1 2', '3+3');
 is $params->param('1 2'), '3+3', 'right value';
 is_deeply $params->to_hash, {foo => '+', '1 2' => '3+3'}, 'right structure';
+$params = Mojo::Parameters->new('a=works+too');
+is "$params", 'a=works+too', 'right format';
+is_deeply $params->to_hash, {a => 'works too'}, 'right structure';
+is $params->param('a'), 'works too', 'right value';
+is "$params", 'a=works+too', 'right format';
 
 # Array values
 $params = Mojo::Parameters->new;
@@ -122,3 +146,8 @@ $params->parse('input=say%20%22%C2%AB%22;');
 is $params->params->[1], 'say "«"', 'right value';
 is $params->param('input'), 'say "«"', 'right value';
 is "$params", 'input=say+%22%C2%AB%22', 'right result';
+
+# Reparse
+$params = Mojo::Parameters->new('foo=bar&baz=23');
+$params->parse('foo=bar&baz=23');
+is "$params", 'foo=bar&baz=23', 'right result';
@@ -0,0 +1,189 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+# Disable IPv6, epoll and kqueue
+BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
+
+use Test::More;
+plan skip_all => 'set TEST_ONLINE to enable this test (developer only!)'
+  unless $ENV{TEST_ONLINE};
+plan tests => 18;
+
+use_ok 'Mojo::IOLoop';
+
+use List::Util 'first';
+use Mojo::IOLoop;
+use Mojo::URL;
+
+# "Your guilty consciences may make you vote Democratic, but secretly you all
+#  yearn for a Republican president to lower taxes, brutalize criminals, and
+#  rule you like a king!"
+my $r = Mojo::IOLoop->singleton->resolver;
+
+# Resolve all record
+my %types;
+$r->resolve(
+  'www.google.com',
+  '*',
+  sub {
+    my ($self, $records) = @_;
+    $types{$_->[0]}++ for @$records;
+    Mojo::IOLoop->stop;
+  }
+);
+Mojo::IOLoop->start;
+ok keys %types > 1, 'multiple record types';
+
+# Lookup
+my $result;
+$r->lookup(
+  'google.com',
+  sub {
+    my ($self, $address) = @_;
+    $result = $address;
+    Mojo::IOLoop->stop;
+  }
+);
+Mojo::IOLoop->start;
+ok $result, 'got an address';
+
+# Resolve TXT record
+$result = undef;
+$r->resolve(
+  'google.com',
+  'TXT',
+  sub {
+    my ($self, $records) = @_;
+    $result = (first { $_->[0] eq 'TXT' } @$records)->[1];
+    Mojo::IOLoop->stop;
+  }
+);
+Mojo::IOLoop->start;
+like $result, qr/spf/, 'right record';
+
+# Resolve NS records
+my $found = 0;
+$r->resolve(
+  'gmail.com',
+  'NS',
+  sub {
+    my ($self, $records) = @_;
+    $found++ if first { $_->[1] =~ /ns\d*.google\.com/ } @$records;
+    Mojo::IOLoop->stop;
+  }
+);
+Mojo::IOLoop->start;
+ok $found, 'found NS records';
+
+# Resolve AAAA record
+$result = undef;
+my $ttl;
+$r->resolve(
+  'ipv6.google.com',
+  'AAAA',
+  sub {
+    my ($self, $records) = @_;
+    $result = (first { $_->[0] eq 'AAAA' } @$records)->[1];
+    $ttl    = (first { $_->[0] eq 'AAAA' } @$records)->[2];
+    Mojo::IOLoop->stop;
+  }
+);
+Mojo::IOLoop->start;
+like $result, $Mojo::URL::IPV6_RE, 'valid IPv6 record';
+ok $ttl, 'got a TTL value';
+
+# Resolve CNAME record
+$result = undef;
+$r->resolve(
+  'ipv6.google.com',
+  'CNAME',
+  sub {
+    my ($self, $records) = @_;
+    $result = (first { $_->[0] eq 'CNAME' } @$records)->[1];
+    Mojo::IOLoop->stop;
+  }
+);
+Mojo::IOLoop->start;
+is $result, 'ipv6.l.google.com', 'right CNAME record';
+
+# Resolve MX records
+$found = 0;
+$r->resolve(
+  'gmail.com',
+  'MX',
+  sub {
+    my ($self, $records) = @_;
+    $found++
+      if first { $_->[1] =~ /gmail-smtp-in\.l\.google\.com/ } @$records;
+    Mojo::IOLoop->stop;
+  }
+);
+Mojo::IOLoop->start;
+ok $found, 'found MX records';
+
+# Resolve A record and perform PTR roundtrip
+my ($a1, $ptr, $a2);
+$r->resolve(
+  'mojolicio.us',
+  'A',
+  sub {
+    my ($self, $records) = @_;
+    $a1 = (first { $_->[0] eq 'A' } @$records)->[1];
+    $self->resolve(
+      $a1, 'PTR',
+      sub {
+        my ($self, $records) = @_;
+        $ptr = $records->[0]->[1];
+        $self->resolve(
+          $ptr, 'A',
+          sub {
+            my ($self, $records) = @_;
+            $a2 = (first { $_->[0] eq 'A' } @$records)->[1];
+            Mojo::IOLoop->stop;
+          }
+        );
+      }
+    );
+  }
+);
+Mojo::IOLoop->start;
+like $a1, $Mojo::URL::IPV4_RE, 'valid IPv4 record';
+is $a1, $a2, 'PTR roundtrip succeeded';
+
+# Resolve PTR record (IPv6)
+$found = 0;
+$r->resolve(
+  '2001:4f8:0:2:0:0:0:e',
+  'PTR',
+  sub {
+    my ($self, $records) = @_;
+    $found++ if first { $_->[1] eq 'freebsd.isc.org' } @$records;
+    Mojo::IOLoop->stop;
+  }
+);
+Mojo::IOLoop->start;
+ok $found, 'found IPv6 PTR record';
+
+# Invalid DNS server
+ok scalar $r->servers, 'got a dns server';
+$r->servers('192.0.2.1', $r->servers);
+is $r->servers, '192.0.2.1', 'new invalid dns server';
+$r->lookup('google.com', sub { Mojo::IOLoop->stop });
+Mojo::IOLoop->start;
+my $fallback = $r->servers;
+isnt $fallback, '192.0.2.1', 'valid dns server';
+$result = undef;
+$r->lookup(
+  'google.com',
+  sub {
+    my ($self, $address) = @_;
+    $result = $address;
+    Mojo::IOLoop->stop;
+  }
+);
+Mojo::IOLoop->start;
+ok $result, 'got an address';
+is scalar $r->servers, $fallback, 'still the same dns server';
+isnt $fallback, '192.0.2.1', 'still valid dns server';
@@ -174,7 +174,7 @@ $output = $mt->render(<<'EOF');
 EOF
 is $output, "<html>\n\n", 'expression block';
 
-# Escaped expression block (extra whitespace)
+# Escaped expression block (passed through with extra whitespace)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 <% my $block =  begin %>
@@ -182,9 +182,10 @@ $output = $mt->render(<<'EOF');
 <% end  %>
 <%== $block->() %>
 EOF
-is $output, "\n\n&lt;html&gt;\n\n", 'escaped expression block';
+is $output, "\n\n<html>\n\n", 'escaped expression block';
 
-# Escaped expression block (perl lines and extra whitespace)
+# Escaped expression block
+# (passed through with perl lines and extra whitespace)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 % my $block =  begin
@@ -192,9 +193,10 @@ $output = $mt->render(<<'EOF');
 <% end  %>
 <%== $block->() %>
 EOF
-is $output, "\n&lt;html&gt;\n\n", 'escaped expression block';
+is $output, "\n<html>\n\n", 'escaped expression block';
 
-# Escaped expression block (indented perl lines and extra whitespace)
+# Escaped expression block
+# (passed through with indented perl lines and extra whitespace)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
  % my $block =  begin
@@ -202,9 +204,9 @@ $output = $mt->render(<<'EOF');
    % end
 <%== $block->() %>
 EOF
-is $output, "&lt;html&gt;\n\n", 'escaped expression block';
+is $output, "<html>\n\n", 'escaped expression block';
 
-# Captured escaped expression block (extra whitespace)
+# Captured escaped expression block (passed through with extra whitespace)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 <%== my $result = capture begin  =%>
@@ -212,9 +214,10 @@ $output = $mt->render(<<'EOF');
 <%  end =%>
 <%= $result =%>
 EOF
-is $output, '&lt;html&gt;<html>', 'captured escaped expression block';
+is $output, '<html><html>', 'captured escaped expression block';
 
-# Captured escaped expression block (perl lines and extra whitespace)
+# Captured escaped expression block
+# (passed through with perl lines and extra whitespace)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 %== my $result = capture  begin
@@ -224,12 +227,13 @@ $output = $mt->render(<<'EOF');
 EOF
 is $output, <<EOF, 'captured escaped expression block';
 
-&lt;html&gt;
+<html>
 
 <html>
 EOF
 
-# Captured escaped expression block (indented perl lines and extra whitespace)
+# Captured escaped expression block
+# (passed through with indented perl lines and extra whitespace)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 %== my $result = capture  begin
@@ -239,12 +243,12 @@ $output = $mt->render(<<'EOF');
 EOF
 is $output, <<EOF, 'captured escaped expression block';
 
-&lt;html&gt;
+<html>
 
 <html>
 EOF
 
-# Capture lines (extra whitespace)
+# Capture lines (passed through with extra whitespace)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 <% my $result = escape capture begin                  %>
@@ -252,46 +256,46 @@ $output = $mt->render(<<'EOF');
 <%                        end %>
 %= $result
 EOF
-is $output, "\n\n&lt;html&gt;\n\n", 'captured lines';
+is $output, "\n\n<html>\n\n", 'captured lines';
 
-# Capture tags
+# Capture tags (passed through)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 <% my $result = escape capture begin %><html><% end %><%= $result %>
 EOF
-is $output, "&lt;html&gt;\n", 'capture tags';
+is $output, "<html>\n", 'capture tags';
 
-# Capture tags (alternative)
+# Capture tags (passed through alternative)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 <% my $result = escape capture begin %><html><% end %><%= $result %>
 EOF
-is $output, "&lt;html&gt;\n", 'capture tags';
+is $output, "<html>\n", 'capture tags';
 
-# Capture tags with appended code
+# Capture tags with appended code (passed through)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 <% my $result = escape( capture begin %><html><% end ); %><%= $result %>
 EOF
-is $output, "&lt;html&gt;\n", 'capture tags with appended code';
+is $output, "<html>\n", 'capture tags with appended code';
 
-# Capture tags with appended code (alternative)
+# Capture tags with appended code (passed through alternative)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 <% my $result = escape( capture begin %><html><% end ); %><%= $result %>
 EOF
-is $output, "&lt;html&gt;\n", 'capture tags with appended code';
+is $output, "<html>\n", 'capture tags with appended code';
 
-# Nested capture tags
+# Nested capture tags (passed through)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 <% my $result = capture
   begin %><%= escape capture begin %><html><% end
   %><% end %><%= $result %>
 EOF
-is $output, "&lt;html&gt;\n", 'nested capture tags';
+is $output, "<html>\n", 'nested capture tags';
 
-# Nested capture tags (alternative)
+# Nested capture tags (passed through alternative)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 <% my $result = capture begin =%>
@@ -301,7 +305,7 @@ $output = $mt->render(<<'EOF');
 <% end =%>
 <%= $result =%>
 EOF
-is $output, '&lt;html&gt;', 'nested capture tags';
+is $output, '<html>', 'nested capture tags';
 
 # Advanced capturing (extra whitespace)
 $mt     = Mojo::Template->new;
@@ -525,13 +529,13 @@ $output = $mt->render(<<'EOF');
 %= __PACKAGE__
 %= foo
 EOF
-is $output, "Mojo::Template::Context\nworks!\n", 'right result';
+is $output, "Mojo::Template::SandBox\nworks!\n", 'right result';
 $output = $mt->render(<<'EOF');
 % BEGIN { MyTemplateExporter->import }
 %= __PACKAGE__
 %= foo
 EOF
-is $output, "Mojo::Template::Context\nworks!\n", 'right result';
+is $output, "Mojo::Template::SandBox\nworks!\n", 'right result';
 
 # Unusable error message (stacktrace required)
 $mt     = Mojo::Template->new;
@@ -5,7 +5,7 @@ use warnings;
 
 use utf8;
 
-use Test::More tests => 321;
+use Test::More tests => 322;
 
 # "I don't want you driving around in a car you built yourself.
 #  You can sit there complaining, or you can knit me some seat belts."
@@ -59,22 +59,23 @@ is "$url", 'http://sri:foobar@kraih.com:8080?monkey=bar&foo=bar#23',
 $url->query('foo');
 is "$url", 'http://sri:foobar@kraih.com:8080?foo#23', 'right format';
 $url->query('foo=bar');
-is "$url", 'http://sri:foobar@kraih.com:8080?foo%3Dbar#23', 'right format';
+is "$url", 'http://sri:foobar@kraih.com:8080?foo=bar#23', 'right format';
 
 # Query string
 $url = Mojo::URL->new(
   'http://sri:foobar@kraih.com:8080?_monkeybiz%3B&_monkey;23#23');
-is $url->is_abs,   1,                              'is absolute';
-is $url->scheme,   'http',                         'right scheme';
-is $url->userinfo, 'sri:foobar',                   'right userinfo';
-is $url->host,     'kraih.com',                    'right host';
-is $url->port,     '8080',                         'right port';
-is $url->path,     '',                             'no path';
-is $url->query,    '_monkeybiz%3B%26_monkey%3B23', 'right query';
-is_deeply $url->query->params, ['_monkeybiz;&_monkey;23', undef],
+is $url->is_abs,   1,                          'is absolute';
+is $url->scheme,   'http',                     'right scheme';
+is $url->userinfo, 'sri:foobar',               'right userinfo';
+is $url->host,     'kraih.com',                'right host';
+is $url->port,     '8080',                     'right port';
+is $url->path,     '',                         'no path';
+is $url->query,    '_monkeybiz%3B&_monkey;23', 'right query';
+is_deeply $url->query->params, ['_monkeybiz;', '', '_monkey', '', 23, ''],
   'right structure';
+is $url->query, '_monkeybiz%3B=&_monkey=&23=', 'right query';
 is $url->fragment, '23', 'right fragment';
-is "$url", 'http://sri:foobar@kraih.com:8080?_monkeybiz%3B%26_monkey%3B23#23',
+is "$url", 'http://sri:foobar@kraih.com:8080?_monkeybiz%3B=&_monkey=&23=#23',
   'right format';
 
 # Relative
@@ -9,7 +9,7 @@ BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
 use Test::More;
 plan skip_all => 'Windows is too fragile for this test!'
   if $^O eq 'MSWin32' || $^O =~ /cygwin/;
-plan tests => 68;
+plan tests => 70;
 
 use_ok 'Mojo::UserAgent';
 
@@ -184,7 +184,7 @@ is $tx->res->code, 200,      'right status';
 is $tx->res->body, 'works!', 'right content';
 
 # Close connection (bypassing safety net)
-Mojo::IOLoop->singleton->_drop_immediately($last);
+Mojo::IOLoop->singleton->_drop($last);
 
 # GET / (mock server closed connection)
 $tx = $ua->get("http://localhost:$port/mock");
@@ -201,7 +201,7 @@ is $tx->res->code, 200,      'right status';
 is $tx->res->body, 'works!', 'right content';
 
 # Close connection (bypassing safety net)
-Mojo::IOLoop->singleton->_drop_immediately($last);
+Mojo::IOLoop->singleton->_drop($last);
 
 # GET / (mock server closed connection)
 $tx = $ua->get("http://localhost:$port/mock");
@@ -294,3 +294,13 @@ $ua->get(
 );
 Mojo::IOLoop->start;
 is_deeply \@kept_alive, [1, 1], 'connections kept alive';
+
+# Premature connection close
+$port = Mojo::IOLoop->generate_port;
+$id   = Mojo::IOLoop->listen(
+  port      => $port,
+  on_accept => sub { shift->drop(shift) }
+);
+$tx = $ua->get("http://localhost:$port/");
+ok !$tx->success, 'not successful';
+is $tx->error, 'Premature connection close.', 'right error';
@@ -33,7 +33,7 @@ $ua->get(
 $loop->start;
 $ua = undef;
 my $ticks     = 0;
-my $recurring = $loop->recurring(sub { $ticks++ });
+my $recurring = $loop->recurring(0 => sub { $ticks++ });
 my $idle      = $loop->idle(sub { $loop->stop });
 $loop->start;
 $loop->drop($recurring);
@@ -4,7 +4,10 @@ use strict;
 use warnings;
 
 # Disable IPv6, epoll and kqueue
-BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
+BEGIN {
+  $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1;
+  $ENV{MOJO_MODE} = 'development';
+}
 
 use Test::More tests => 249;
 
@@ -24,9 +27,6 @@ use_ok 'MojoliciousTest';
 
 my $t = Test::Mojo->new(app => 'MojoliciousTest');
 
-my $backup = $ENV{MOJO_MODE} || '';
-$ENV{MOJO_MODE} = 'development';
-
 # Foo::fun
 my $url = $t->build_url;
 $url->path('/fun/time');
@@ -329,5 +329,3 @@ $t->get_ok('/foo/session')->status_is(200)
 # Mixed formats
 $t->get_ok('/rss.xml')->status_is(200)->content_type_is('application/rss+xml')
   ->content_like(qr/<\?xml version="1.0" encoding="UTF-8"\?><rss \/>/);
-
-$ENV{MOJO_MODE} = $backup;
@@ -93,7 +93,7 @@ my $tx = Mojo::Transaction::HTTP->new;
 $tx->req->method('GET');
 $tx->req->url->parse('/not_found');
 $c->tx($tx);
-is $d->dispatch($c), 1, 'dispatched';
+is $d->dispatch($c), undef, 'not dispatched';
 is_deeply $c->stash, {}, 'empty stash';
 ok !$c->render_called, 'nothing rendered';
 
@@ -104,7 +104,7 @@ $tx->req->method('POST');
 $tx->req->url->parse('/foo/hello');
 $c->tx($tx);
 $c->stash(test => 23);
-is $d->dispatch($c), undef, 'dispatched';
+is $d->dispatch($c), 1, 'dispatched';
 is $c->stash->{controller}, 'foo',   'right value';
 is $c->stash->{action},     'bar',   'right value';
 is $c->stash->{capture},    'hello', 'right value';
@@ -152,7 +152,7 @@ $tx = Mojo::Transaction::HTTP->new;
 $tx->req->method('GET');
 $tx->req->url->parse('/foo/hello%20there');
 $c->tx($tx);
-is $d->dispatch($c), undef, 'dispatched';
+is $d->dispatch($c), 1, 'dispatched';
 is $c->stash->{controller}, 'foo',         'right value';
 is $c->stash->{action},     'bar',         'right value';
 is $c->stash->{capture},    'hello there', 'right value';
@@ -168,7 +168,7 @@ $tx = Mojo::Transaction::HTTP->new;
 $tx->req->method('GET');
 $tx->req->url->parse('/foo/%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82');
 $c->tx($tx);
-is $d->dispatch($c), undef, 'dispatched';
+is $d->dispatch($c), 1, 'dispatched';
 is $c->stash->{controller}, 'foo',          'right value';
 is $c->stash->{action},     'bar',          'right value';
 is $c->stash->{capture},    'привет', 'right value';
@@ -3,10 +3,15 @@
 use strict;
 use warnings;
 
+use utf8;
+
 # Disable IPv6, epoll and kqueue
-BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
+BEGIN {
+  $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1;
+  $ENV{MOJO_MODE} = 'testing';
+}
 
-use Test::More tests => 41;
+use Test::More tests => 113;
 
 use FindBin;
 use lib "$FindBin::Bin/lib";
@@ -32,8 +37,6 @@ get '/hello' => sub {
 package MyTestApp::Test1;
 use Mojolicious::Lite;
 
-use Mojo::IOLoop;
-
 # GET /yada (embedded)
 get '/yada' => sub {
   my $self = shift;
@@ -85,6 +88,17 @@ plugin 'PluginWithEmbeddedApp';
 
 app->routes->namespace('MyTestApp');
 
+# Mount full external application a few times
+use FindBin;
+my $external = "$FindBin::Bin/external/myapp.pl";
+plugin mount => {'/x/1' => $external};
+plugin(mount => ('/x/♥' => $external))->to(message => 'works 2!');
+plugin mount => {'mojolicious.org' => $external};
+plugin mount => {'MOJOLICIO.US/'   => $external};
+plugin mount => {'*.kraih.com'     => $external};
+plugin(mount => ('*.foo-bar.de/♥/123' => $external))
+  ->to(message => 'works 3!');
+
 # GET /hello
 get '/hello' => 'works';
 
@@ -117,6 +131,8 @@ app->routes->route('/hello')->detour(TestApp::app())->to(name => 'embedded');
 # GET /just/* (external embedded app)
 get('/just' => {name => 'working'})->detour('EmbeddedTestApp');
 
+get '/host' => {text => 'main application!'};
+
 my $t = Test::Mojo->new;
 
 # GET /foo/bar (plugin app)
@@ -170,6 +186,97 @@ $t->get_ok('/third')->status_is(200)
 # GET /just/works (from external embedded app)
 $t->get_ok('/just/works')->status_is(200)->content_is("It is working!\n");
 
+# GET /x/1/ (full external application)
+$t->get_ok('/x/1/')->status_is(200)->content_is("works!\n\ntoo!works!!!\n");
+
+# GET /x/1/index.html (full external application)
+$t->get_ok('/x/1/index.html')->status_is(200)
+  ->content_is('External static file!');
+
+# GET /x/1/echo (full external application)
+$t->get_ok('/x/1/echo')->status_is(200)->content_is('echo: nothing!');
+
+# GET /x/1/stream (full external application)
+$t->get_ok('/x/1/stream')->status_is(200)->content_is('hello!');
+
+# GET /x/1/url/☃ (full external application)
+$t->get_ok('/x/1/url/☃')->status_is(200)
+  ->content_is('/x/1/url/%E2%98%83 -> /x/1/%E2%98%83/stream!');
+
+# GET /x/♥/ (full external application)
+$t->get_ok('/x/♥/')->status_is(200)->content_is("works!\n\ntoo!works!!!\n");
+
+# GET /x/♥/index.html (full external application)
+$t->get_ok('/x/♥/index.html')->status_is(200)
+  ->content_is('External static file!');
+
+# GET /x/♥/echo (full external application)
+$t->get_ok('/x/♥/echo')->status_is(200)->content_is('echo: works 2!');
+
+# GET /x/♥/stream (full external application)
+$t->get_ok('/x/♥/stream')->status_is(200)->content_is('hello!');
+
+# GET /x/♥/url/☃ (full external application)
+$t->get_ok('/x/♥/url/☃')->status_is(200)
+  ->content_is(
+  '/x/%E2%99%A5/url/%E2%98%83 -> /x/%E2%99%A5/%E2%98%83/stream!');
+
+# GET /host (main application)
+$t->get_ok('/host')->status_is(200)->content_is('main application!');
+
+# GET / (full external application with domain)
+$t->get_ok('/' => {Host => 'mojolicious.org'})->status_is(200)
+  ->content_is("works!\n\ntoo!works!!!\n");
+
+# GET /host (full external application with domain)
+$t->get_ok('/host' => {Host => 'mojolicious.org'})->status_is(200)
+  ->content_is('mojolicious.org');
+
+# GET / (full external application with domain)
+$t->get_ok('/' => {Host => 'mojolicio.us'})->status_is(200)
+  ->content_is("works!\n\ntoo!works!!!\n");
+
+# GET /host (full external application with domain)
+$t->get_ok('/host' => {Host => 'mojolicio.us'})->status_is(200)
+  ->content_is('mojolicio.us');
+
+# GET / (full external application with domain)
+$t->get_ok('/' => {Host => 'kraih.com'})->status_is(200)
+  ->content_is("works!\n\ntoo!works!!!\n");
+
+# GET /host (full external application with domain)
+$t->get_ok('/host' => {Host => 'KRaIH.CoM'})->status_is(200)
+  ->content_is('kraih.com');
+
+# GET /host (full external application with wildcard domain)
+$t->get_ok('/host' => {Host => 'www.kraih.com'})->status_is(200)
+  ->content_is('www.kraih.com');
+
+# GET /host (full external application with wildcard domain)
+$t->get_ok('/host' => {Host => 'foo.bar.kraih.com'})->status_is(200)
+  ->content_is('foo.bar.kraih.com');
+
+# GET /♥/123/host (full external application with a bit of everything)
+$t->get_ok('/♥/123/' => {Host => 'foo-bar.de'})->status_is(200)
+  ->content_is("works!\n\ntoo!works!!!\n");
+
+# GET /♥/123/host (full external application with a bit of everything)
+$t->get_ok('/♥/123/host' => {Host => 'foo-bar.de'})->status_is(200)
+  ->content_is('foo-bar.de');
+
+# GET /♥/123/echo (full external application with a bit of everything)
+$t->get_ok('/♥/123/echo' => {Host => 'foo-bar.de'})->status_is(200)
+  ->content_is('echo: works 3!');
+
+# GET /♥/123/host (full external application with a bit of everything)
+$t->get_ok('/♥/123/host' => {Host => 'www.foo-bar.de'})->status_is(200)
+  ->content_is('www.foo-bar.de');
+
+# GET /♥/123/echo (full external application with a bit of everything)
+$t->get_ok('/♥/123/echo' => {Host => 'www.foo-bar.de'})->status_is(200)
+  ->content_is('echo: works 3!');
+
 __DATA__
+
 @@ works.html.ep
 Hello from the main app!
@@ -4,13 +4,12 @@ use strict;
 use warnings;
 
 # Disable IPv6, epoll and kqueue
-BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
+BEGIN {
+  $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1;
+  $ENV{MOJO_MODE} = 'development';
+}
 
-# Development
-my $backup;
-BEGIN { $backup = $ENV{MOJO_MODE} || ''; $ENV{MOJO_MODE} = 'development' }
-
-use Test::More tests => 28;
+use Test::More tests => 32;
 
 # "This calls for a party, baby.
 #  I'm ordering 100 kegs, 100 hookers and 100 Elvis impersonators that aren't
@@ -26,6 +25,9 @@ get '/dead_template';
 # GET /dead_included_template
 get '/dead_included_template';
 
+# GET /dead_template_with_layout
+get '/dead_template_with_layout';
+
 # GET /dead_action
 get '/dead_action' => sub { die 'dead action!' };
 
@@ -76,8 +78,12 @@ $t->get_ok('/dead_template')->status_is(500)->content_like(qr/1\./)
 $t->get_ok('/dead_included_template')->status_is(500)->content_like(qr/1\./)
   ->content_like(qr/dead\ template!/);
 
+# GET /dead_template_with_layout
+$t->get_ok('/dead_template_with_layout')->status_is(500)
+  ->content_like(qr/2\./)->content_like(qr/dead\ template\ with\ layout!/);
+
 # GET /dead_action
-$t->get_ok('/dead_action')->status_is(500)->content_like(qr/26\./)
+$t->get_ok('/dead_action')->status_is(500)->content_like(qr/32\./)
   ->content_like(qr/dead\ action!/);
 
 # GET /double_dead_action
@@ -90,9 +96,10 @@ $t->get_ok('/trapped')->status_is(200)->content_is('bar');
 # GET /trapped/too
 $t->get_ok('/trapped/too')->status_is(200)->content_is('works');
 
-$ENV{MOJO_MODE} = $backup;
-
 __DATA__
+@@ layouts/green.html.ep
+%= content
+
 @@ dead_template.html.ep
 % die 'dead template!';
 
@@ -100,3 +107,7 @@ __DATA__
 this
 %= include 'dead_template'
 works!
+
+@@ dead_template_with_layout.html.ep
+% layout 'green';
+% die 'dead template with layout!';
@@ -3,6 +3,8 @@
 use strict;
 use warnings;
 
+use utf8;
+
 # "Boy, who knew a cooler could also make a handy wang coffin?"
 use Mojolicious::Lite;
 
@@ -12,7 +14,38 @@ plugin 'config';
 # GET /
 get '/' => 'index';
 
+# GET /echo
+get '/echo' => sub {
+  my $self = shift;
+  $self->render_text('echo: ' . ($self->stash('message') || 'nothing!'));
+};
+
+# GET /stream
+get '/stream' => sub {
+  shift->write_chunk(
+    'he',
+    sub {
+      shift->write_chunk('ll', sub { shift->finish('o!') });
+    }
+  );
+};
+
+# GET /url/☃
+get '/url/☃' => sub {
+  my $self  = shift;
+  my $route = $self->url_for;
+  my $rel   = $self->url_for('/☃/stream');
+  $self->render_text("$route -> $rel!");
+};
+
+# GET /host
+get '/host' => sub {
+  my $self = shift;
+  $self->render(text => $self->url_for->base->host);
+};
+
 app->start;
 __DATA__
-@@ index.html.ep
-<%= $config->{just} . $config->{works} . stash('also') %>
+
+@@ menubar.html.ep
+<%= $config->{just} %>
@@ -0,0 +1,2 @@
+%= include 'menubar'
+<%= $config->{works} . stash('also') %>
\ No newline at end of file
@@ -12,7 +12,7 @@ BEGIN {
 }
 
 # "Who are you, and why should I care?"
-use Test::More tests => 6;
+use Test::More tests => 15;
 
 # "Of all the parasites I've had over the years,
 #  these worms are among the best."
@@ -23,8 +23,18 @@ use Test::Mojo;
 my $t = Test::Mojo->new;
 
 # GET /
-$t->get_ok('/')->status_is(200)->content_is("works!too!works!!!\n");
+$t->get_ok('/')->status_is(200)->content_is("works!\n\ntoo!works!!!\n");
 
 # GET /index.html
 $t->get_ok('/index.html')->status_is(200)
   ->content_is('External static file!');
+
+# GET /echo
+$t->get_ok('/echo')->status_is(200)->content_is('echo: nothing!');
+
+# GET /stream
+$t->get_ok('/stream')->status_is(200)->content_is('hello!');
+
+# GET /url/☃
+$t->get_ok('/url/☃')->status_is(200)
+  ->content_is('/url/%E2%98%83 -> /%E2%98%83/stream!');
@@ -14,7 +14,7 @@ sub this_one_dies { die "doh!\n" }
 
 sub this_one_might_die {
   die "double doh!\n" unless shift->req->headers->header('X-DoNotDie');
-  return 1;
+  1;
 }
 
 1;
@@ -41,7 +41,7 @@ sub stage1 {
 
   # Fail
   $self->render_text('Go away!');
-  return;
+  undef;
 }
 
 sub stage2 {
@@ -6,13 +6,12 @@ use warnings;
 use utf8;
 
 # Disable IPv6, epoll and kqueue
-BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
+BEGIN {
+  $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1;
+  $ENV{MOJO_MODE} = 'development';
+}
 
-# Development
-my $backup;
-BEGIN { $backup = $ENV{MOJO_MODE} || ''; $ENV{MOJO_MODE} = 'development' }
-
-use Test::More tests => 792;
+use Test::More tests => 886;
 
 # Pollution
 123 =~ m/(\d+)/;
@@ -37,8 +36,10 @@ use Test::Mojo;
 # User agent
 my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton, app => app);
 
-# Header condition plugin
-plugin 'header_condition';
+# Missing plugin
+eval { plugin 'does_not_exist'; };
+is $@, "Plugin \"does_not_exist\" missing, maybe you need to install it?\n",
+  'right error';
 
 # Plugin with a template
 use FindBin;
@@ -58,6 +59,12 @@ is app->test_helper2, 'Mojolicious::Controller', 'right value';
 # Test renderer
 app->renderer->add_handler(dead => sub { die 'renderer works!' });
 
+# GET /☃
+get '/☃' => sub {
+  my $self = shift;
+  $self->render_text($self->url_for . $self->url_for('current'));
+};
+
 # GET /unicode/a%E4b
 get '/unicode/aäb' => sub {
   my $self = shift;
@@ -70,9 +77,39 @@ get '/unicode/:stuff' => sub {
   $self->render(text => $self->param('stuff') . $self->url_for);
 };
 
+# GET /conditional
+get '/conditional' => (
+  cb => sub {
+    my ($r, $c, $captures) = @_;
+    $captures->{condition} = $c->req->headers->header('X-Condition');
+    return unless $captures->{condition};
+    1;
+  }
+) => {inline => '<%= $condition %>'};
+
 # GET /
 get '/' => 'root';
 
+# GET /alternatives/☃
+# GET /alternatives/♥
+get '/alternatives/:char' => [char => [qw/☃ ♥/]] => sub {
+  my $self = shift;
+  $self->render_text($self->url_for);
+};
+
+# GET /alterformat
+# GET /alterformat.json
+get '/alterformat' => [format => ['json']] => {format => 'json'} => sub {
+  my $self = shift;
+  $self->render_text($self->stash('format'));
+};
+
+# GET /noformat
+get '/noformat' => [format => undef] => {format => 'xml'} => sub {
+  my $self = shift;
+  $self->render_text($self->stash('format') . $self->url_for);
+};
+
 # DELETE /
 del sub { shift->render(text => 'Hello!') };
 
@@ -217,9 +254,12 @@ get '/finished' => sub {
 get '/привет/мир' =>
   sub { shift->render(text => 'привет мир') };
 
-# GET /root
+# GET /root.html
 get '/root.html' => 'root_path';
 
+# GET /root
+get '/root' => sub { shift->render_text('root fallback!') };
+
 # GET /template.txt
 get '/template.txt' => 'template';
 
@@ -253,7 +293,12 @@ get '/inline/ep/partial' => sub {
 };
 
 # GET /source
-get '/source' => sub { shift->render_static('../lite_app.t') };
+get '/source' => sub {
+  my $self = shift;
+  my $file = $self->param('fail') ? 'does_not_exist.txt' : '../lite_app.t';
+  $self->render_static($file)
+    or $self->render_text('does not exist!', status => 404);
+};
 
 # GET /foo_relaxed/*
 get '/foo_relaxed/(.test)' => sub {
@@ -599,7 +644,7 @@ under sub {
   return unless $self->req->headers->header('X-Bender');
   $self->res->headers->add('X-Under' => 23);
   $self->res->headers->add('X-Under' => 24);
-  return 1;
+  1;
 };
 
 # GET /with_under
@@ -623,7 +668,7 @@ under sub {
 
   # Not authenticated
   $self->render('param_auth_denied');
-  return;
+  undef;
 };
 
 # GET /param_auth
@@ -639,18 +684,32 @@ under sub {
   $self->cookie(foo => 'cookie', {expires => (time + 60)});
   $self->signed_cookie(bar => 'signed_cookie', {expires => (time + 120)});
   $self->cookie(bad => 'bad_cookie--12345678');
-  return 1;
+  1;
 };
 
 # GET /bridge2stash
 get '/bridge2stash' =>
   sub { shift->render(template => 'bridge2stash', handler => 'ep'); };
 
+# Make sure after_dispatch can make session changes
+hook after_dispatch => sub {
+  my $self = shift;
+  return unless $self->req->url->path =~ /^\/late\/session/;
+  $self->session(late => 'works!');
+};
+
+# GET /late/session
+get '/late/session' => sub {
+  my $self = shift;
+  my $late = $self->session('late') || 'not yet!';
+  $self->render_text($late);
+};
+
 # Counter
 my $under = 0;
 under sub {
   shift->res->headers->header('X-Under' => ++$under);
-  return 1;
+  1;
 };
 
 # GET /with_under_count
@@ -659,7 +718,7 @@ get '/with/under/count';
 # Everything gets past this
 under sub {
   shift->res->headers->header('X-Possible' => 1);
-  return 1;
+  1;
 };
 
 # GET /possible
@@ -668,7 +727,7 @@ get '/possible' => 'possible';
 # Nothing gets past this
 under sub {
   shift->res->headers->header('X-Impossible' => 1);
-  return 0;
+  0;
 };
 
 # GET /impossible
@@ -706,6 +765,12 @@ $tua->ioloop->timer(
   }
 );
 
+# GET /☃
+$t->get_ok('/☃')->status_is(200)->content_is('/%E2%98%83/%E2%98%83');
+
+# GET /☃ (with trailing slash)
+$t->get_ok('/☃/')->status_is(200)->content_is('/%E2%98%83//%E2%98%83/');
+
 # GET /unicode/a%E4b
 $t->get_ok('/unicode/a%E4b')->status_is(200)->content_is('/unicode/a%E4b');
 
@@ -719,6 +784,13 @@ $t->get_ok('/unicode/a b')->status_is(200)->content_is('a b/unicode/a%20b');
 # GET /unicode/a\b
 $t->get_ok('/unicode/a\\b')->status_is(200)->content_is('a\\b/unicode/a%5Cb');
 
+# GET /conditional
+$t->get_ok('/conditional' => {'X-Condition' => 'Conditions rock!'})
+  ->status_is(200)->content_is("Conditions rock!\n");
+
+# GET /conditional (missing header)
+$t->get_ok('/conditional')->status_is(404)->content_is("Oops!\n");
+
 # GET /
 $t->get_ok('/')->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
   ->header_is('X-Powered-By' => 'Mojolicious (Perl)')
@@ -743,6 +815,59 @@ $t->delete_ok('/')->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
 $t->post_ok('/')->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
   ->header_is('X-Powered-By' => 'Mojolicious (Perl)')->content_is('Bye!');
 
+# GET /alternatives/☃
+$t->get_ok('/alternatives/☃')->status_is(200)
+  ->header_is(Server         => 'Mojolicious (Perl)')
+  ->header_is('X-Powered-By' => 'Mojolicious (Perl)')
+  ->content_is('/alternatives/%E2%98%83');
+
+# GET /alternatives/♥
+$t->get_ok('/alternatives/♥')->status_is(200)
+  ->header_is(Server         => 'Mojolicious (Perl)')
+  ->header_is('X-Powered-By' => 'Mojolicious (Perl)')
+  ->content_is('/alternatives/%E2%99%A5');
+
+# GET /alternatives/☃23 (invalid alternative)
+$t->get_ok('/alternatives/☃23')->status_is(404)
+  ->header_is(Server         => 'Mojolicious (Perl)')
+  ->header_is('X-Powered-By' => 'Mojolicious (Perl)')->content_is("Oops!\n");
+
+# GET /alternatives (invalid alternative)
+$t->get_ok('/alternatives')->status_is(404)
+  ->header_is(Server         => 'Mojolicious (Perl)')
+  ->header_is('X-Powered-By' => 'Mojolicious (Perl)')->content_is("Oops!\n");
+
+# GET /alternatives/test (invalid alternative)
+$t->get_ok('/alternatives/test')->status_is(404)
+  ->header_is(Server         => 'Mojolicious (Perl)')
+  ->header_is('X-Powered-By' => 'Mojolicious (Perl)')->content_is("Oops!\n");
+
+# GET /alterformat
+$t->get_ok('/alterformat')->status_is(200)
+  ->header_is(Server         => 'Mojolicious (Perl)')
+  ->header_is('X-Powered-By' => 'Mojolicious (Perl)')->content_is('json');
+
+# GET /alterformat.json
+$t->get_ok('/alterformat.json')->status_is(200)
+  ->header_is(Server         => 'Mojolicious (Perl)')
+  ->header_is('X-Powered-By' => 'Mojolicious (Perl)')->content_is('json');
+
+# GET /alterformat.html (invalid format)
+$t->get_ok('/alterformat.html')->status_is(404)
+  ->header_is(Server         => 'Mojolicious (Perl)')
+  ->header_is('X-Powered-By' => 'Mojolicious (Perl)')->content_is("Oops!\n");
+
+# GET /noformat
+$t->get_ok('/noformat')->status_is(200)
+  ->header_is(Server         => 'Mojolicious (Perl)')
+  ->header_is('X-Powered-By' => 'Mojolicious (Perl)')
+  ->content_is('xml/noformat');
+
+# GET /noformat.xml
+$t->get_ok('/noformat.xml')->status_is(404)
+  ->header_is(Server         => 'Mojolicious (Perl)')
+  ->header_is('X-Powered-By' => 'Mojolicious (Perl)')->content_is("Oops!\n");
+
 # POST /multipart/form ("application/x-www-form-urlencoded")
 $t->post_form_ok('/multipart/form', {test => [1 .. 5]})->status_is(200)
   ->content_is(join "\n", 1 .. 5);
@@ -959,11 +1084,22 @@ $t->get_ok('/привет/мир')->status_is(200)
   ->content_type_is('text/html;charset=UTF-8')
   ->content_is('привет мир');
 
-# GET /root
+# GET /root.html
 $t->get_ok('/root.html')->status_is(200)
   ->header_is(Server         => 'Mojolicious (Perl)')
   ->header_is('X-Powered-By' => 'Mojolicious (Perl)')->content_is("/\n");
 
+# GET /root (fallback route without format)
+$t->get_ok('/root')->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
+  ->header_is('X-Powered-By' => 'Mojolicious (Perl)')
+  ->content_is('root fallback!');
+
+# GET /root.txt (fallback route without format)
+$t->get_ok('/root.txt')->status_is(200)
+  ->header_is(Server         => 'Mojolicious (Perl)')
+  ->header_is('X-Powered-By' => 'Mojolicious (Perl)')
+  ->content_is('root fallback!');
+
 # GET /.html
 $t->get_ok('/.html')->status_is(200)
   ->header_is(Server         => 'Mojolicious (Perl)')
@@ -972,9 +1108,9 @@ $t->get_ok('/.html')->status_is(200)
   "/root.html\n/root.html\n/root.html\n/root.html\n/root.html\n");
 
 # GET /0 ("X-Forwarded-For")
+my $source = $t->tx->local_address;
 $t->get_ok('/0', {'X-Forwarded-For' => '192.168.2.2, 192.168.2.1'})
-  ->status_is(200)
-  ->content_like(qr/http\:\/\/localhost\:\d+\/0\-127\.0\.0\.1\-0/);
+  ->status_is(200)->content_like(qr/http\:\/\/localhost\:\d+\/0\-$source\-0/);
 
 # GET /0 (reverse proxy with "X-Forwarded-For")
 my $backup2 = $ENV{MOJO_REVERSE_PROXY};
@@ -986,27 +1122,26 @@ $ENV{MOJO_REVERSE_PROXY} = $backup2;
 
 # GET /0 ("X-Forwarded-Host")
 $t->get_ok('/0', {'X-Forwarded-Host' => 'mojolicio.us:8080'})->status_is(200)
-  ->content_like(qr/http\:\/\/localhost\:\d+\/0\-127\.0\.0\.1\-0/);
+  ->content_like(qr/http\:\/\/localhost\:\d+\/0\-$source\-0/);
 
 # GET /0 (reverse proxy with "X-Forwarded-Host")
 $backup2 = $ENV{MOJO_REVERSE_PROXY};
 $ENV{MOJO_REVERSE_PROXY} = 1;
 $t->get_ok('/0', {'X-Forwarded-Host' => 'mojolicio.us:8080'})->status_is(200)
-  ->content_is('http://mojolicio.us:8080/0-127.0.0.1-0');
+  ->content_is("http://mojolicio.us:8080/0-$source-0");
 $ENV{MOJO_REVERSE_PROXY} = $backup2;
 
 # GET /0 ("X-Forwarded-HTTPS" and "X-Forwarded-Host")
 $t->get_ok('/0',
   {'X-Forwarded-HTTPS' => 1, 'X-Forwarded-Host' => 'mojolicio.us'})
-  ->status_is(200)
-  ->content_like(qr/http\:\/\/localhost\:\d+\/0\-127\.0\.0\.1\-0/);
+  ->status_is(200)->content_like(qr/http\:\/\/localhost\:\d+\/0\-$source\-0/);
 
 # GET /0 (reverse proxy with "X-Forwarded-HTTPS" and "X-Forwarded-Host")
 $backup2 = $ENV{MOJO_REVERSE_PROXY};
 $ENV{MOJO_REVERSE_PROXY} = 1;
 $t->get_ok('/0',
   {'X-Forwarded-HTTPS' => 1, 'X-Forwarded-Host' => 'mojolicio.us'})
-  ->status_is(200)->content_is('https://mojolicio.us/0-127.0.0.1-0');
+  ->status_is(200)->content_is("https://mojolicio.us/0-$source-0");
 $ENV{MOJO_REVERSE_PROXY} = $backup2;
 
 # DELETE /inline/epl
@@ -1025,6 +1160,9 @@ $t->get_ok('/inline/ep/partial')->status_is(200)
 # GET /source
 $t->get_ok('/source')->status_is(200)->content_like(qr/get_ok\('\/source/);
 
+# GET /source (file does not exist)
+$t->get_ok('/source?fail=1')->status_is(404)->content_is('does not exist!');
+
 # GET / (with body and max message size)
 $backup2 = $ENV{MOJO_MAX_MESSAGE_SIZE} || '';
 $ENV{MOJO_MAX_MESSAGE_SIZE} = 1024;
@@ -1551,13 +1689,31 @@ $t->get_ok('/redirect/condition/1' => {'X-Condition-Test' => 1})
 $t->get_ok('/bridge2stash' => {'X-Flash' => 1})->status_is(200)
   ->content_is("stash too!!!!!!!!\n");
 
+# GET /bridge2stash (with cookies, session and flash)
+$t->get_ok('/bridge2stash')->status_is(200)
+  ->content_is(
+  "stash too!cookie!signed_cookie!!bad_cookie--12345678!session!flash!/!\n");
+
+# GET /bridge2stash (broken session cookie)
+$t->reset_session;
+my $session = b("☃☃☃☃☃")->b64_encode('');
+my $hmac    = $session->clone->hmac_md5_sum($t->ua->app->secret);
+my $broken  = "\$Version=1; mojolicious=$session--$hmac; \$Path=/";
+$t->get_ok('/bridge2stash' => {Cookie => $broken})->status_is(200)
+  ->content_is("stash too!!!!!!!/!\n");
+
+# GET /bridge2stash (fresh start)
+$t->reset_session;
+$t->get_ok('/bridge2stash' => {'X-Flash' => 1})->status_is(200)
+  ->content_is("stash too!!!!!!!!\n");
+
 # GET /favicon.ico (random static requests)
 $t->get_ok('/favicon.ico')->status_is(200);
 $t->get_ok('/mojolicious-white.png')->status_is(200);
 $t->get_ok('/mojolicious-black.png')->status_is(200);
 $t->get_ok('/favicon.ico')->status_is(200);
 
-# GET /bridge2stash (with cookies, session and flash)
+# GET /bridge2stash (with cookies, session and flash again)
 $t->get_ok('/bridge2stash')->status_is(200)
   ->content_is(
   "stash too!cookie!signed_cookie!!bad_cookie--12345678!session!flash!/!\n");
@@ -1571,6 +1727,15 @@ $t->get_ok('/bridge2stash' => {'X-Flash2' => 1})->status_is(200)
 $t->get_ok('/bridge2stash')->status_is(200)
   ->content_is("stash too!cookie!signed_cookie!!bad_cookie--12345678!!!!\n");
 
+# GET /late/session (late session does not affect rendering)
+$t->get_ok('/late/session')->status_is(200)->content_is('not yet!');
+
+# GET /late/session (previous late session does affect rendering)
+$t->get_ok('/late/session')->status_is(200)->content_is('works!');
+
+# GET /late/session (previous late session does affect rendering again)
+$t->get_ok('/late/session')->status_is(200)->content_is('works!');
+
 # GET /with/under/count
 $t->get_ok('/with/under/count', {'X-Bender' => 'Rodriguez'})->status_is(200)
   ->header_is(Server         => 'Mojolicious (Perl)')
@@ -1650,8 +1815,6 @@ is $timer,
   "/root.html\n/root.html\n/root.html\n/root.html\n/root.html\nworks!",
   'right content';
 
-$ENV{MOJO_MODE} = $backup;
-
 __DATA__
 @@ with-format.html.ep
 <%= url_for 'without-format' %>
@@ -6,11 +6,10 @@ use warnings;
 use utf8;
 
 # Disable IPv6, epoll and kqueue
-BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
-
-# Development
-my $backup;
-BEGIN { $backup = $ENV{MOJO_MODE} || ''; $ENV{MOJO_MODE} = 'development' }
+BEGIN {
+  $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1;
+  $ENV{MOJO_MODE} = 'development';
+}
 
 use Test::More tests => 9;
 
@@ -63,12 +63,12 @@ is $result, undef, 'no result';
 
 # Format
 $pattern = Mojolicious::Routes::Pattern->new('/(controller)test/(action)');
-is $pattern->format, undef, 'no value';
+is $pattern->defaults->{format}, undef, 'no value';
 $pattern =
   Mojolicious::Routes::Pattern->new('/(:controller)test/:action.html');
-is $pattern->format, 'html', 'right value';
+is $pattern->defaults->{format}, 'html', 'right value';
 $pattern = Mojolicious::Routes::Pattern->new('/index.cgi');
-is $pattern->format, 'cgi', 'right value';
+is $pattern->defaults->{format}, 'cgi', 'right value';
 
 # Relaxed
 $pattern = Mojolicious::Routes::Pattern->new('/test/(.controller)/:action');
@@ -79,7 +79,7 @@ is $pattern->render({controller => 'foo.bar', action => 'baz'}),
   '/test/foo.bar/baz', 'right result';
 $pattern = Mojolicious::Routes::Pattern->new('/test/(.groovy)');
 $result  = $pattern->match('/test/foo.bar');
-is $pattern->format, undef, 'no value';
+is $pattern->defaults->{format}, undef, 'no value';
 is $result->{groovy}, 'foo.bar', 'right value';
 is $result->{format}, undef,     'no value';
 is $pattern->render({groovy => 'foo.bar'}), '/test/foo.bar', 'right result';
@@ -4,7 +4,10 @@ use strict;
 use warnings;
 
 # Disable IPv6, epoll and kqueue
-BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
+BEGIN {
+  $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1;
+  $ENV{MOJO_MODE} = 'production';
+}
 
 use Test::More tests => 51;
 
@@ -18,9 +21,6 @@ use_ok 'MojoliciousTest';
 
 my $t = Test::Mojo->new(app => 'MojoliciousTest');
 
-my $backup = $ENV{MOJO_MODE} || '';
-$ENV{MOJO_MODE} = 'production';
-
 # SyntaxError::foo in production mode (syntax error in controller)
 $t->get_ok('/syntax_error/foo')->status_is(500)
   ->header_is(Server         => 'Mojolicious (Perl)')
@@ -81,5 +81,3 @@ $t->get_ok('/../../mojolicious/secret.txt')->status_is(404)
   ->header_is(Server         => 'Mojolicious (Perl)')
   ->header_is('X-Powered-By' => 'Mojolicious (Perl)')
   ->content_like(qr/Not Found/);
-
-$ENV{MOJO_MODE} = $backup;
@@ -3,7 +3,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 237;
+use Test::More tests => 313;
 
 # "They're not very heavy, but you don't hear me not complaining."
 use_ok 'Mojolicious::Routes';
@@ -21,6 +21,17 @@ $r->route('/clean/too')->to(something => 1);
 # /0
 $r->route('/0')->to(null => 1);
 
+# /alternatives
+# /alternatives/0
+# /alternatives/test
+# /alternatives/23
+$r->route('/alternatives/:foo', foo => [qw/0 test 23/])->to(foo => 11);
+
+# /alternatives2/0
+# /alternatives2/test
+# /alternatives2/23
+$r->route('/alternatives2/:foo', foo => [qw/0 test 23/]);
+
 # /*/test
 my $test = $r->route('/:controller/test')->to(action => 'test');
 
@@ -90,6 +101,28 @@ $r->route('/format3/:foo.html')->to(controller => 'me', action => 'bye');
 # /format3/*.json
 $r->route('/format3/:foo.json')->to(controller => 'me', action => 'bye_json');
 
+# /format4.txt
+$r->route('/format4', format => qr/txt/)
+  ->to(controller => 'we', action => 'howdy');
+
+# /format5.txt
+# /format5.text
+$r->route('/format5', format => [qw/txt text/])
+  ->to(controller => 'we', action => 'cheers');
+
+# /format6
+# /format6.html
+$r->route('/format6', format => ['html'])
+  ->to(controller => 'us', action => 'yay', format => 'html');
+
+# /format7
+$r->route('/format7', format => undef)
+  ->to(controller => 'us', action => 'wow');
+
+# /format8
+$r->route('/format8', format => undef)
+  ->to(controller => 'us', action => 'doh', format => 'xml');
+
 # /articles
 # /articles.html
 # /articles/1
@@ -162,6 +195,56 @@ $m = Mojolicious::Routes::Match->new(get => '/0')->match($r);
 is $m->stack->[0]->{null}, 1, 'right value';
 is $m->path_for, '/0', 'right path';
 
+# Alternatives with default
+$m = Mojolicious::Routes::Match->new(get => '/alternatives')->match($r);
+is $m->stack->[0]->{foo}, 11, 'right value';
+is @{$m->stack}, 1, 'right number of elements';
+is $m->path_for, '/alternatives', 'right path';
+$m = Mojolicious::Routes::Match->new(get => '/alternatives/0')->match($r);
+is $m->stack->[0]->{foo}, 0, 'right value';
+is @{$m->stack}, 1, 'right number of elements';
+is $m->path_for, '/alternatives/0', 'right path';
+$m = Mojolicious::Routes::Match->new(get => '/alternatives/test')->match($r);
+is $m->stack->[0]->{foo}, 'test', 'right value';
+is @{$m->stack}, 1, 'right number of elements';
+is $m->path_for, '/alternatives/test', 'right path';
+$m = Mojolicious::Routes::Match->new(get => '/alternatives/23')->match($r);
+is $m->stack->[0]->{foo}, 23, 'right value';
+is @{$m->stack}, 1, 'right number of elements';
+is $m->path_for, '/alternatives/23', 'right path';
+$m = Mojolicious::Routes::Match->new(get => '/alternatives/24')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(get => '/alternatives/tset')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(get => '/alternatives/00')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+is $m->path_for('alternativesfoo'), '/alternatives', 'right path';
+
+# Alternatives without default
+$m = Mojolicious::Routes::Match->new(get => '/alternatives2')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(get => '/alternatives2/0')->match($r);
+is $m->stack->[0]->{foo}, 0, 'right value';
+is @{$m->stack}, 1, 'right number of elements';
+is $m->path_for, '/alternatives2/0', 'right path';
+$m = Mojolicious::Routes::Match->new(get => '/alternatives2/test')->match($r);
+is $m->stack->[0]->{foo}, 'test', 'right value';
+is @{$m->stack}, 1, 'right number of elements';
+is $m->path_for, '/alternatives2/test', 'right path';
+$m = Mojolicious::Routes::Match->new(get => '/alternatives2/23')->match($r);
+is $m->stack->[0]->{foo}, 23, 'right value';
+is @{$m->stack}, 1, 'right number of elements';
+is $m->path_for, '/alternatives2/23', 'right path';
+$m = Mojolicious::Routes::Match->new(get => '/alternatives2/24')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(get => '/alternatives2/tset')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(get => '/alternatives2/00')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+is $m->path_for('alternatives2foo'), '/alternatives2', 'right path';
+is $m->path_for('alternatives2foo', foo => 0), '/alternatives2/0',
+  'right path';
+
 # Real world example using most features at once
 $m = Mojolicious::Routes::Match->new(get => '/articles.html')->match($r);
 is $m->stack->[0]->{controller}, 'articles', 'right value';
@@ -389,6 +472,68 @@ is $m->stack->[0]->{format},     'json',     'right value';
 is $m->stack->[0]->{foo},        'baz',      'right value';
 is $m->path_for, '/format3/baz.json', 'right path';
 is @{$m->stack}, 1, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format4')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format4.txt')->match($r);
+is $m->stack->[0]->{controller}, 'we',    'right value';
+is $m->stack->[0]->{action},     'howdy', 'right value';
+is $m->stack->[0]->{format},     'txt',   'right value';
+is $m->path_for, '/format4.txt', 'right path';
+is @{$m->stack}, 1, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format4.html')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format4.txt.txt')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format5')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format5.txt')->match($r);
+is $m->stack->[0]->{controller}, 'we',     'right value';
+is $m->stack->[0]->{action},     'cheers', 'right value';
+is $m->stack->[0]->{format},     'txt',    'right value';
+is $m->path_for, '/format5.txt', 'right path';
+is @{$m->stack}, 1, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format5.text')->match($r);
+is $m->stack->[0]->{controller}, 'we',     'right value';
+is $m->stack->[0]->{action},     'cheers', 'right value';
+is $m->stack->[0]->{format},     'text',   'right value';
+is $m->path_for, '/format5.text', 'right path';
+is @{$m->stack}, 1, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format5.html')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format5.txt.txt')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format6')->match($r);
+is $m->stack->[0]->{controller}, 'us',   'right value';
+is $m->stack->[0]->{action},     'yay',  'right value';
+is $m->stack->[0]->{format},     'html', 'right value';
+is $m->path_for, '/format6.html', 'right path';
+is @{$m->stack}, 1, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format6.html')->match($r);
+is $m->stack->[0]->{controller}, 'us',   'right value';
+is $m->stack->[0]->{action},     'yay',  'right value';
+is $m->stack->[0]->{format},     'html', 'right value';
+is $m->path_for, '/format6.html', 'right path';
+is @{$m->stack}, 1, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format6.txt')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format6.txt.html')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format7')->match($r);
+is $m->stack->[0]->{controller}, 'us',  'right value';
+is $m->stack->[0]->{action},     'wow', 'right value';
+is $m->stack->[0]->{format},     undef, 'no value';
+is $m->path_for, '/format7', 'right path';
+is @{$m->stack}, 1, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format7.html')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format8')->match($r);
+is $m->stack->[0]->{controller}, 'us',  'right value';
+is $m->stack->[0]->{action},     'doh', 'right value';
+is $m->stack->[0]->{format},     'xml', 'right value';
+is $m->path_for, '/format8', 'right path';
+is @{$m->stack}, 1, 'right number of elements';
+$m = Mojolicious::Routes::Match->new(GET => '/format8.xml')->match($r);
+is @{$m->stack}, 0, 'right number of elements';
 
 # Request methods
 $m = Mojolicious::Routes::Match->new(get => '/method/get.html')->match($r);
@@ -6,7 +6,7 @@ use warnings;
 # Disable IPv6, epoll and kqueue
 BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
 
-use Test::More tests => 30;
+use Test::More tests => 45;
 
 # "Hey! Bite my glorious golden ass!"
 use Mojolicious::Lite;
@@ -27,6 +27,9 @@ get 'style';
 # GET /basicform
 get '/basicform';
 
+# GET /multibox
+get '/multibox';
+
 # GET /form
 get 'form/:test' => 'form';
 
@@ -92,8 +95,53 @@ $t->get_ok('/basicform')->status_is(200)->content_is(<<EOF);
 </form>
 EOF
 
+# GET /multibox
+$t->get_ok('/multibox')->status_is(200)->content_is(<<EOF);
+<form action="/multibox">
+  <input name="foo" type="checkbox" value="one" />
+  <input name="foo" type="checkbox" value="two" />
+  <input type="submit" value="Ok" />
+</form>
+EOF
+
+# GET /multibox (with one value)
+$t->get_ok('/multibox?foo=two')->status_is(200)->content_is(<<EOF);
+<form action="/multibox">
+  <input name="foo" type="checkbox" value="one" />
+  <input checked="checked" name="foo" type="checkbox" value="two" />
+  <input type="submit" value="Ok" />
+</form>
+EOF
+
+# GET /multibox (with one right and one wrong value)
+$t->get_ok('/multibox?foo=one&foo=three')->status_is(200)->content_is(<<EOF);
+<form action="/multibox">
+  <input checked="checked" name="foo" type="checkbox" value="one" />
+  <input name="foo" type="checkbox" value="two" />
+  <input type="submit" value="Ok" />
+</form>
+EOF
+
+# GET /multibox (with wrong value)
+$t->get_ok('/multibox?foo=bar')->status_is(200)->content_is(<<EOF);
+<form action="/multibox">
+  <input name="foo" type="checkbox" value="one" />
+  <input name="foo" type="checkbox" value="two" />
+  <input type="submit" value="Ok" />
+</form>
+EOF
+
+# GET /multibox (with two values)
+$t->get_ok('/multibox?foo=two&foo=one')->status_is(200)->content_is(<<EOF);
+<form action="/multibox">
+  <input checked="checked" name="foo" type="checkbox" value="one" />
+  <input checked="checked" name="foo" type="checkbox" value="two" />
+  <input type="submit" value="Ok" />
+</form>
+EOF
+
 # GET /form
-$t->get_ok('/form/lala?a=b&b=0&c=2&d=3&escaped=1%22+%222')->status_is(200)
+$t->get_ok('/form/lala?a=2&b=0&c=2&d=3&escaped=1%22+%222')->status_is(200)
   ->content_is(<<EOF);
 <form action="/links" method="post">
   <input name="foo" />
@@ -119,8 +167,8 @@ $t->get_ok('/form/lala?a=b&b=0&c=2&d=3&escaped=1%22+%222')->status_is(200)
   <input name="foo" />
 </form>
 <input name="escaped" value="1&quot; &quot;2" />
-<input name="a" value="b" />
-<input name="a" value="b" />
+<input name="a" value="2" />
+<input name="a" value="2" />
 EOF
 
 # GET /form (alternative)
@@ -263,6 +311,13 @@ __DATA__
   %= submit_button
 %= end
 
+@@ multibox.html.ep
+%= form_for multibox => begin
+  %= check_box foo => 'one'
+  %= check_box foo => 'two'
+  %= submit_button
+%= end
+
 @@ form.html.ep
 <%= form_for 'links', method => 'post' => begin %>
   <%= input_tag 'foo' %>
@@ -4,7 +4,10 @@ use strict;
 use warnings;
 
 # Disable IPv6, epoll and kqueue
-BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
+BEGIN {
+  $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1;
+  $ENV{MOJO_MODE} = 'testing';
+}
 
 use Test::More tests => 26;
 
@@ -18,9 +21,6 @@ use_ok 'MojoliciousTest';
 
 my $t = Test::Mojo->new(app => 'MojoliciousTest');
 
-my $backup = $ENV{MOJO_MODE} || '';
-$ENV{MOJO_MODE} = 'testing';
-
 # SyntaxError::foo in testing mode (syntax error in controller)
 $t->get_ok('/syntax_error/foo')->status_is(500)
   ->header_is(Server         => 'Mojolicious (Perl)')
@@ -51,5 +51,3 @@ $t->get_ok('/../../mojolicious/secret.txt')->status_is(404)
   ->header_is(Server         => 'Mojolicious (Perl)')
   ->header_is('X-Powered-By' => 'Mojolicious (Perl)')
   ->content_like(qr/Testing not found/);
-
-$ENV{MOJO_MODE} = $backup;